/* =============================================================================
   DEX Portal - Components

   Everything that lives inside the content region: page furniture, buttons,
   the data table, the right-hand slide-over panel, form controls and the
   sign-in screen.
   ============================================================================= */


/* =============================================================================
   PAGE FURNITURE
   ============================================================================= */

/* Full bleed: the page fills the whole content region, edge to edge, with no
   card frame or rounded corners. */
.page-surface {
    height: 100%;
    background: var(--surface-panel);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.page-header {
    flex: none;
    display: flex;
    align-items: flex-start;
    gap: 20px;
    padding: var(--gutter-tight) var(--gutter);
    border-bottom: 1px solid var(--border-subtle);
}

.page-header__text {
    flex: 1;
    min-width: 0;
}

.page-header__title {
    font-size: var(--font-size-large);
    font-weight: 600;
    letter-spacing: -0.02em;
    display: flex;
    align-items: center;
    gap: 10px;
}

.page-header__description {
    font-size: 13px;
    color: var(--text-muted);
    margin-top: 5px;
    line-height: 1.55;
}

.page-header__actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: none;
}

.page-toolbar {
    flex: none;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: var(--gutter-tight) var(--gutter);
    border-bottom: 1px solid var(--border-subtle);
    flex-wrap: wrap;
}

.record-count {
    font-size: 12.5px;
    color: var(--text-muted);
    margin-left: auto;
    white-space: nowrap;
}

/* The body of a full-bleed page. `.page-surface` is a fixed-height flex column
   that clips, so a page whose content is taller than the stage needs one child
   that claims the leftover height and scrolls it - without `min-height: 0` a
   flex child refuses to shrink below its content and the scroll never appears.
   The gutter matches `.page-header`, so the body lines up with the title above
   it; the top is tight because the header's rule already separates them. */
.page-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: var(--gutter-tight) var(--gutter) var(--gutter);
}


/* =============================================================================
   BUTTONS
   ============================================================================= */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: var(--control-height);
    padding: 0 16px;
    border-radius: var(--radius-button);
    font-size: var(--font-size-base);
    font-weight: 600;
    letter-spacing: -0.005em;
    white-space: nowrap;
    transition:
        background   var(--duration-instant) var(--easing-standard),
        border-color var(--duration-instant) var(--easing-standard),
        color        var(--duration-instant) var(--easing-standard),
        transform    var(--duration-instant) var(--easing-standard);
}

.button:active:not(:disabled) {
    transform: scale(0.98);
}

.button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.button--primary {
    background: var(--accent);
    color: var(--text-on-dark);
    border: 1px solid var(--accent);
}

.button--primary:hover:not(:disabled) {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
}

.button--secondary {
    background: var(--surface-panel);
    color: var(--text-secondary);
    border: 1px solid var(--border-strong);
}

.button--secondary:hover:not(:disabled) {
    background: var(--surface-subtle);
    color: var(--text-primary);
}

.button--subtle {
    background: var(--border-subtle);
    color: var(--text-secondary);
    border: 1px solid transparent;
}

.button--subtle:hover:not(:disabled) {
    background: #e7eaed;
    color: var(--text-primary);
}

.button--danger {
    background: var(--surface-panel);
    color: var(--status-danger);
    border: 1px solid #f3c9c5;
}

.button--danger:hover:not(:disabled) {
    background: var(--status-danger-tint);
}

.button--small {
    height: var(--control-height-small);
    padding: 0 12px;
    font-size: var(--font-size-small);
}

.button--full-width {
    width: 100%;
}


/* =============================================================================
   FILTER FIELD - the search box that sits above a data table
   ============================================================================= */

.filter-field {
    display: flex;
    align-items: center;
    gap: 9px;
    height: var(--control-height);
    width: 320px;
    max-width: 100%;
    padding: 0 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
    color: var(--text-muted);
    transition: border-color var(--duration-instant) var(--easing-standard);
}

.filter-field:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-tint);
}

.filter-field__input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: 0;
    background: none;
    font: inherit;
    font-size: var(--font-size-base);
    color: var(--text-primary);
}

.filter-field__input::placeholder {
    color: var(--text-muted);
}


/* =============================================================================
   DATA TABLE
   ============================================================================= */

.data-table-scroll {
    flex: 1;
    overflow: auto;
    min-height: 0;
}

.data-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--font-size-base);
}

.data-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--surface-subtle);
    text-align: left;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 11px var(--gutter-tight);
    border-bottom: 1px solid var(--border-default);
    white-space: nowrap;
    user-select: none;
}

.data-table thead th.data-table__sortable {
    cursor: pointer;
}

.data-table thead th.data-table__sortable:hover {
    color: var(--text-secondary);
}

.data-table__sort-indicator {
    display: inline-block;
    margin-left: 5px;
    opacity: 0.85;
}

.data-table tbody td {
    padding: 12px var(--gutter-tight);
    border-bottom: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    vertical-align: middle;
}

.data-table tbody tr {
    transition: background var(--duration-instant) var(--easing-standard);
    cursor: pointer;
}

.data-table tbody tr:hover {
    background: var(--surface-subtle);
}

.data-table__reference {
    font-family: var(--font-monospace);
    font-size: 12px;
    color: var(--text-muted);
    white-space: nowrap;
}

.data-table__person {
    display: flex;
    align-items: center;
    gap: 11px;
}

.data-table__person-name {
    font-weight: 600;
    color: var(--text-primary);
}

.data-table__person-detail {
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 1px;
}

.person-avatar {
    width: 34px;
    height: 34px;
    border-radius: var(--radius-medium);
    flex: none;
    display: grid;
    place-items: center;
    font-size: 12px;
    font-weight: 700;
    color: var(--text-on-dark);
    background: var(--brand-gradient);
    letter-spacing: 0.02em;
    overflow: hidden;
}

/* Every staff photo in the product, whatever wears it. The wrapper decides how
   big a face is and the photo only ever fills it, so a 600px portrait cannot
   size a table row. Corners are inherited because some wrappers cannot clip
   with overflow - the header badge hangs its clock status dot outside itself. */
.avatar-photo {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    object-fit: cover;
}

.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 24px;
    padding: 0 10px;
    border-radius: var(--radius-button);
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
}

.status-pill::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
}

.status-pill--active      { background: var(--status-success-tint); color: var(--status-success); }
.status-pill--invited     { background: var(--status-info-tint);    color: var(--status-info); }
.status-pill--suspended   { background: var(--status-warning-tint); color: var(--status-warning); }
.status-pill--deactivated { background: var(--status-neutral-tint); color: var(--status-neutral); }

/* An answer that has not arrived yet. Distinct from "not connected" on purpose:
   showing a failure state while the question is still in flight teaches people
   to ignore the badge. */
.status-pill--pending {
    background: var(--status-neutral-tint);
    color: var(--text-muted);
}

.status-pill--pending::before {
    animation: status-pill-pending-pulse 1.2s var(--easing-standard) infinite;
}

@keyframes status-pill-pending-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.3; }
}

.two-factor-indicator {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    font-weight: 500;
}

.two-factor-indicator--enrolled     { color: var(--status-success); }
.two-factor-indicator--not-enrolled { color: var(--text-muted); }


/* =============================================================================
   EMPTY, LOADING AND ERROR STATES
   ============================================================================= */

.state-message {
    flex: 1;
    display: grid;
    place-items: center;
    padding: 48px 24px;
}

.state-message__content {
    text-align: center;
    max-width: 400px;
}

.state-message__glyph {
    width: 64px;
    height: 64px;
    border-radius: var(--radius-button);
    margin: 0 auto 20px;
    display: grid;
    place-items: center;
    color: var(--text-on-dark);
    background: var(--brand-gradient);
    box-shadow: 0 10px 28px -10px rgba(124, 92, 255, 0.7);
}

.state-message__glyph--neutral {
    background: var(--border-subtle);
    color: var(--text-muted);
    box-shadow: none;
}

.state-message__glyph--danger {
    background: var(--status-danger-tint);
    color: var(--status-danger);
    box-shadow: none;
}

.state-message__title {
    font-size: 17px;
    font-weight: 600;
    letter-spacing: -0.02em;
    margin-bottom: 7px;
}

.state-message__description {
    font-size: 13.5px;
    color: var(--text-muted);
    line-height: 1.6;
}

.state-message__route {
    display: inline-block;
    margin-top: 18px;
    padding: 7px 14px;
    border-radius: var(--radius-pill);
    background: var(--border-subtle);
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 500;
    font-family: var(--font-monospace);
}

.state-message__action {
    margin-top: 20px;
}

@keyframes spinnerRotate {
    to { transform: rotate(360deg); }
}

.loading-spinner {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2.5px solid var(--border-default);
    border-top-color: var(--accent);
    margin: 0 auto 16px;
    animation: spinnerRotate 700ms linear infinite;
}


/* =============================================================================
   SLIDE-OVER PANEL

   Opens from the right for every create and edit action in the product.
   ============================================================================= */

/* The panel and its scrim start below the top header rather than at the top of
   the window. The header carries the global controls - search, notifications,
   the account menu - and those stay reachable while a record is open. */
.slide-over-scrim {
    position: fixed;
    top: var(--height-top-header);
    right: 0;
    bottom: 0;
    left: 0;
    background: rgba(10, 12, 16, 0.44);
    backdrop-filter: blur(3px);
    opacity: 0;
    pointer-events: none;
    z-index: 100;
    transition: opacity var(--duration-panel-close) var(--easing-standard);
}

.slide-over-scrim--visible {
    opacity: 1;
    pointer-events: auto;
}

.slide-over {
    position: fixed;
    top: var(--height-top-header);
    right: 0;
    bottom: 0;
    width: var(--width-slide-over-panel);
    min-width: var(--width-slide-over-minimum);
    max-width: 96vw;
    background: var(--surface-panel);
    box-shadow: var(--shadow-slide-over);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    z-index: 101;
    transition:
        transform var(--duration-panel-close) var(--easing-standard),
        width     var(--duration-panel-close) var(--easing-standard);
}

.slide-over--open {
    transform: translateX(0);
    transition:
        transform var(--duration-panel-open)  var(--easing-standard),
        width     var(--duration-panel-close) var(--easing-standard);
}

/* Clicking the grip throws the panel out to 80vw. Deliberately short of the full
   window: the remaining strip of scrim is what keeps it reading as a panel over
   the page rather than as a navigation away from it. */
.slide-over--expanded {
    width: 80vw !important;
}

/* -----------------------------------------------------------------------------
   WIDE VARIANT

   For records: two columns of content and a thread beside them. No `!important`
   here, unlike the compact variant - dragging the panel has to keep working,
   and the inline width a drag sets should still win. Clearing that width by
   double clicking the grip is what lands back on this number, which is the
   whole point of expressing the reset width as a class rather than a constant.
   -------------------------------------------------------------------------- */

.slide-over--wide {
    width: var(--width-slide-over-wide);
}

/* A slim opening width for single-column panels like an invoice. No
   !important: the class sets where it opens and resets to, and a drag's inline
   width still wins afterwards. The 440px floor keeps it usable on laptops. */
.slide-over--narrow {
    width: 30vw;
}

/* Slimmer again, for read-mostly panels like a subscription. Same rules as
   narrow: the class is the opening and reset width, drags still win. */
.slide-over--quarter {
    width: 25vw;
}

/* -----------------------------------------------------------------------------
   COMPACT VARIANT

   For panels whose content has a natural narrow width - a dial pad is the
   obvious one. Forcing a phone keypad to fill 55vw would leave a column of keys
   marooned in a field of white.

   `!important` because the remembered width is an inline style, and a panel
   opened compact must not inherit whatever width the user last dragged a Users
   panel to. The minimum is lowered too: the product-wide 440px floor exists so
   two-column forms stay usable, which does not apply here.
   ----------------------------------------------------------------------------- */
.slide-over--compact {
    width: 20vw !important;
    min-width: 330px;
    max-width: 420px;
}

/* Resizing a fixed-purpose panel makes no sense, so the grip goes. */
.slide-over--compact .slide-over__resize-handle,
.slide-over--compose .slide-over__resize-handle,
.slide-over--automation .slide-over__resize-handle {
    display: none;
}

/* -----------------------------------------------------------------------------
   AUTOMATION VARIANT

   Every automation rule editor opens at this one width, on every module: the
   notification / comment / stage forms drawn in the rule panel, the task rule
   in the New Task panel, the email rule in the compose panel, the event rule
   in the calendar panel. They are one object wearing different fields, and
   four different widths for the same job is how a product stops feeling like
   one product. Fixed-purpose like the dialler, so no grip - the form has one
   right width, and the boards it opens from need the room beside it.

   The floor keeps the Run block's "1 minutes after Current time" row on one
   line on a laptop; below that it wraps and the timing stops reading as a
   sentence.
   ----------------------------------------------------------------------------- */
.slide-over--automation {
    width: 30vw !important;
    min-width: 420px;
    max-width: none;
}

/* -----------------------------------------------------------------------------
   Breathing room inside an automation editor.

   `.form-field` carries no vertical rhythm of its own product-wide - nearly
   every other form puts its fields in a grid that provides the gap. The rule
   editors stack fields directly on top of each other, so without this a label
   sits flush against the control above it and the whole panel reads as one
   dense block. Scoped to the variant, so no other form in the product moves by
   a pixel.

   `> * + *` rather than a list of pairs: the editors mix fields, the sticky
   Run block, checkboxes and hint paragraphs in an order that changes per
   action, and naming every combination would go stale the next time one gains
   a row. The section header is exempted - it already carries its own
   margin-bottom, and both together read as a gap nobody asked for.
   ----------------------------------------------------------------------------- */
.slide-over--automation .form-section > * + * {
    margin-top: 16px;
}

.slide-over--automation .form-section > .form-section__header + * {
    margin-top: 0;
}

.slide-over--automation .form-section {
    padding: 22px 0;
}

/* The Run / Conditions block itself: a tinted card whose contents were packed
   tighter than the fields around it, which made it the densest thing on screen
   precisely where the thinking happens.

   Deliberately NOT `margin: 0` here. That is the same specificity as the
   `> * + *` rhythm above and comes later in the file, so it silently won and
   the block sat flush against the field above it - which is most of what made
   this panel feel cramped in the first place. Setting only margin-bottom
   leaves margin-top to the rhythm rule when the block follows something, and
   to the base -4px pull when it is the first thing in the panel. */
.slide-over--automation .automation-sticky {
    gap: 18px;
    margin-bottom: 0;
    padding: 16px;
}

.slide-over--automation .automation-sticky__block {
    gap: 12px;
}

/* Radio rows are clickable targets, not just text - the padding is as much
   about the hit area as the look. */
.slide-over--automation .rule-run__option {
    gap: 10px;
    padding: 2px 0;
}

.slide-over--automation .rule-conditions {
    gap: 10px;
}

.slide-over--automation .rule-condition {
    gap: 8px;
}

/* A hint explains the control above it, so it sits closer to that control than
   to whatever follows - the 16px above would otherwise orphan it between the
   two and leave it looking like it belongs to neither. */
.slide-over--automation .form-section > .invoice-hint,
.slide-over--automation .form-section > .task-muted {
    margin-top: 8px;
}

/* The calendar event rule wears .cal-form-section instead; same rhythm. */
.slide-over--automation .cal-form-section > * + * {
    margin-top: 14px;
}

/* -----------------------------------------------------------------------------
   COMPOSE VARIANT

   Fixed-purpose like compact, but sized for writing an email: room for a
   mobile-width (480px) template to render and be edited at its true size,
   while still fitting beside a project record. Still not resizable: the
   form has one right width, the same argument as the keypad.
   ----------------------------------------------------------------------------- */
.slide-over--compose {
    width: 40vw !important;
    min-width: 560px;
    max-width: 720px;
}

/* -----------------------------------------------------------------------------
   SECONDARY PANEL

   A companion view that opens beside an already-open panel rather than over it.
   The primary is translated left by exactly this width, so the two sit flush and
   the pair still reads as one piece of furniture rather than two panels fighting
   for the same edge.

   It sits *below* the primary in the stacking order. During the open and close
   transitions the two briefly overlap, and the primary passing over the top of
   the secondary reads as the primary making room; the reverse reads as a glitch.

   No scrim of its own: the primary's scrim is already covering the page, and a
   second one would darken the first panel as though it had been dismissed.
   ----------------------------------------------------------------------------- */
.slide-over--secondary {
    width: var(--width-slide-over-secondary);
    min-width: var(--width-slide-over-secondary);
    max-width: var(--width-slide-over-secondary);
    z-index: 100;
    border-left: 1px solid var(--border-subtle);
}

/* A companion that carries content with its own natural width keeps that width
   rather than the companion default. The dialler is the case: opened over the
   page or opened beside a client record, it has to be the same object, and a
   keypad stretched an extra forty pixels is how somebody decides it is a
   different one.

   The combination needs saying explicitly. `.slide-over--compact` sets
   `max-width` without `!important`, and a `min-width` from this variant would
   otherwise win - min-width beats max-width in CSS - leaving the panel at the
   companion width regardless. */
.slide-over--secondary.slide-over--compact {
    width: 20vw !important;
    min-width: 330px;
    max-width: 420px;
}

/* An automation editor is 30% whether it opened over the page or beside the
   board that spawned it - that is the whole point of the variant. Stated
   explicitly for the same reason compact is. */
.slide-over--secondary.slide-over--automation {
    width: 30vw !important;
    min-width: 420px;
    max-width: none;
}

/* Below the tablet breakpoint there is no room for two panels side by side, so
   the companion takes the screen. Shrinking both would leave neither legible. */
@media (max-width: 900px) {
    .slide-over--secondary,
    .slide-over--secondary.slide-over--compact {
        width: 100vw !important;
        min-width: 0;
        max-width: 100vw;
        z-index: 102;
    }

    /* Listed separately: the automation rules above carry `!important` and a
       420px floor, either of which would otherwise survive into phone width. */
    .slide-over--automation,
    .slide-over--secondary.slide-over--automation {
        width: 100vw !important;
        min-width: 0;
        max-width: 100vw;
    }
}

/* While the grip is being dragged, nothing may animate - a transition on width
   would make the panel lag behind the pointer and feel like treacle. */
.slide-over--resizing,
.slide-over--resizing * {
    transition: none !important;
    user-select: none;
}


/* -----------------------------------------------------------------------------
   DRAG-TO-RESIZE GRIP

   A wide invisible hit area along the left edge, with a small visible pill
   centred in it. The hit area is deliberately larger than the pill so the
   pointer does not have to be precise.
   ----------------------------------------------------------------------------- */

.slide-over__resize-handle {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 14px;
    cursor: col-resize;
    z-index: 2;
    display: grid;
    place-items: center;
    touch-action: none;
}

.slide-over__resize-grip {
    width: 26px;
    height: 46px;
    border-radius: var(--radius-pill);
    background: var(--surface-panel);
    border: 1px solid var(--border-default);
    box-shadow: -4px 0 14px -6px rgba(16, 24, 40, 0.3);
    display: grid;
    place-items: center;
    color: var(--text-muted);
    transform: translateX(-50%);
    transition:
        background var(--duration-instant) var(--easing-standard),
        color      var(--duration-instant) var(--easing-standard),
        border-color var(--duration-instant) var(--easing-standard),
        transform  var(--duration-instant) var(--easing-standard);
}

.slide-over__resize-handle:hover .slide-over__resize-grip,
.slide-over--resizing .slide-over__resize-grip {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-dark);
    transform: translateX(-50%) scaleY(1.12);
}

/* The whole document takes the resize cursor during a drag, so it does not
   flicker back to an arrow when the pointer outruns the handle. */
body.is-resizing-slide-over,
body.is-resizing-slide-over * {
    cursor: col-resize !important;
}

/* The header is a fixed height whether or not the panel has an identity badge.
   Without this it is as tall as its contents, so a panel with a badge and a
   panel without one put their titles at two different heights - which is
   glaring the moment two panels are open side by side. */
.slide-over__header {
    flex: none;
    display: flex;
    align-items: center;
    gap: 14px;
    min-height: var(--height-slide-over-header);
    padding: var(--gutter-tight) var(--gutter);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);

    /* Dark, so the panel announces itself. Everything inside the header has
       to be told about it - a control that inherits --text-muted here is
       invisible - which is what the rules below are doing. */
    background: var(--panel-header);
    color: var(--text-on-dark);
}

/* The identity block that sits at the left of the panel header. */
.slide-over__identity {
    width: 46px;
    height: 46px;
    border-radius: var(--radius-button);
    flex: none;
    display: grid;
    place-items: center;
    background: var(--brand-gradient);
    color: var(--text-on-dark);
    font-weight: 700;
    font-size: 15px;
    overflow: hidden;
    box-shadow: 0 8px 20px -8px rgba(124, 92, 255, 0.75);
}

/* Telephony panels. See --gradient-call for why they are not brand-coloured. */
.slide-over__identity--call {
    background: var(--gradient-call);
    box-shadow: 0 8px 20px -8px rgba(10, 127, 67, 0.75);
}

/* The header badge is hidden on every panel except the telephony ones. The
   dialler (and its recordings) keep their green badge; everywhere else the
   title leads the header on its own, with no icon or avatar beside it. */
.slide-over__identity:not(.slide-over__identity--call) {
    display: none;
}

.slide-over__heading {
    flex: 1;
    min-width: 0;
}

.slide-over__title {
    font-size: var(--font-size-medium);
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--text-on-dark);
}

.slide-over__subtitle {
    font-size: 13px;
    color: rgba(255, 255, 255, 0.6);
    margin-top: 4px;
    line-height: 1.5;
}

.slide-over__close-button {
    width: 34px;
    height: 34px;
    border-radius: var(--radius-button);
    display: grid;
    place-items: center;
    color: rgba(255, 255, 255, 0.68);
    flex: none;
    transition:
        background var(--duration-instant) var(--easing-standard),
        color      var(--duration-instant) var(--easing-standard);
}

.slide-over__close-button:hover {
    background: rgba(255, 255, 255, 0.14);
    color: var(--text-on-dark);
}

/* -----------------------------------------------------------------------------
   HEADER ACTIONS

   What you do to the record, as opposed to what you do to the panel. Calling
   and emailing live here because they are the reason somebody opened the record
   at speed, and the footer is already three clicks of eye travel away.
   -------------------------------------------------------------------------- */

.slide-over__header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: none;
    margin-right: 4px;
}

.slide-over__header-action {
    width: 34px;
    height: 34px;
    border: none;
    border-radius: var(--radius-medium);
    background: none;
    display: grid;
    place-items: center;
    color: rgba(255, 255, 255, 0.72);
    flex: none;
    cursor: pointer;
    transition:
        background var(--duration-instant) var(--easing-standard),
        color      var(--duration-instant) var(--easing-standard);
}

.slide-over__header-action:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.14);
    color: var(--text-on-dark);
}

/* A green button that starts a call is the one piece of colour convention every
   telephone ever made has already taught everybody. */
.slide-over__header-action--call {
    background: var(--gradient-call-soft);
    color: var(--text-on-dark);
}

.slide-over__header-action--call:hover:not(:disabled) {
    background: var(--gradient-call-soft);
    color: var(--text-on-dark);
    filter: brightness(1.05);
}

/* Email is tinted rather than filled. Two solid buttons side by side would each
   claim to be the main thing to do, and calling is the one that starts a
   conversation the other end has to answer. */
.slide-over__header-action--email {
    background: var(--status-info-tint);
    color: var(--status-info);
}

.slide-over__header-action--email:hover:not(:disabled) {
    background: var(--status-info-tint);
    color: var(--status-info);
    filter: brightness(0.96);
}

/* Disabled rather than hidden: a client with no number still has a place where
   the call button lives, and a row of icons that changes length between records
   makes people aim twice. */
.slide-over__header-action:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.slide-over__header-create {
    background: var(--status-info);
    color: var(--text-on-dark);
    gap: 6px;
}

.slide-over__header-create:hover:not(:disabled) {
    background: var(--status-info);
    filter: brightness(1.08);
}

/* Call, email, Create, Document and the status pill sit in one evenly spaced
   row - the single gap on the flex container governs, with no extra margin
   between groups, so the five read as equally spaced. */
.slide-over__header-action + .record-create,
.slide-over__header-action + .slide-over__header-create,
.slide-over__header-action + .invoice-header-status,
.invoice-header-status + .slide-over__header-action,
.slide-over__header-create + .invoice-header-status {
    margin-left: 0;
}

/* Create, Document and the status pill read as one row of equal buttons: the
   same height as any small button and a shared minimum width, so the status
   control (e.g. "Active") is the size of the Document button beside it. */
.slide-over__header-actions .slide-over__header-create,
.slide-over__header-actions .invoice-header-status,
.slide-over__header-actions .project-status-select {
    height: var(--control-height-small);
    min-width: 116px;
    box-sizing: border-box;
}

.slide-over__header-actions .slide-over__header-create {
    justify-content: center;
}

.slide-over__header-actions .project-status-select {
    text-align: center;
    text-align-last: center;
    padding-left: 14px;
}

/* The header status pill reads as a solid white button: same radius as the
   header buttons, its status colour kept as the text. Applies to every panel
   header (project, invoice, subscription) that carries a status pill. */
.slide-over__header-actions .invoice-header-status {
    background: #fff;
    border-radius: var(--radius-medium);
}

/* The project status control.
   It WAS a native <select> wearing the status-pill skin, and it never looked
   right: the closed control is the browser's, the open list is the operating
   system's, and neither takes the colours the record is painted in. It is now
   a button that opens the same `record-create__menu` the Document button uses,
   so every dropdown in the panel is one dropdown. */
.project-status-select {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    border: none;
    padding: 0 12px;
    cursor: pointer;
    font: inherit;
    font-weight: 600;
}

.project-status-select__caret {
    flex-shrink: 0;
    opacity: 0.75;
}

.slide-over__header-actions .project-status-select {
    background-color: #fff;
}

.project-status-select:focus-visible {
    outline: none;
    box-shadow: 0 0 0 1.5px currentColor;
}

/* The statuses menu: one row per status, its own colour as a dot, a tick on
   the one the project is at. */
.record-create__menu--statuses {
    min-width: 190px;
}

.record-create__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}

.record-create__item--current {
    font-weight: 600;
}

.record-create__tick {
    margin-left: auto;
    display: inline-flex;
    color: var(--accent-primary, #6d5cff);
}

/* The Create button's dropdown: new invoice / new subscription. */
.record-create {
    position: relative;
    display: inline-flex;
}

.record-create__menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 120;
    min-width: 190px;
    padding: 6px;
    background: var(--surface-panel, #fff);
    border: 1px solid var(--border-subtle, #e4e8ef);
    border-radius: var(--radius-button);
    box-shadow: 0 14px 34px rgba(20, 20, 45, 0.16);
}

.record-create__item {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 9px 10px;
    border: none;
    border-radius: var(--radius-button);
    background: transparent;
    color: var(--text-primary, #0f1115);
    font-size: 13.5px;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

.record-create__item:hover {
    background: var(--surface-muted, #f6f8fb);
}

.record-create__item .icon {
    color: var(--text-muted, #8b929b);
    flex-shrink: 0;
}

/* A menu long enough to need sections gets them: the Document menu lists what
   already exists above what can be made. Anything past ten items scrolls
   rather than running off the bottom of the panel. */
.record-create__menu {
    max-height: min(60vh, 420px);
    overflow-y: auto;
}

.record-create__heading {
    padding: 8px 10px 4px;
    color: var(--text-muted, #8b929b);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.record-create__heading + .record-create__item { margin-top: 0; }

.record-create__note {
    padding: 9px 10px;
    color: var(--text-muted, #8b929b);
    font-size: 12.5px;
    line-height: 1.45;
}

/* Between the two halves of the Document menu - documents above, forms
   below. A rule rather than a gap: the gap alone reads as a rendering
   accident, and these are two different things to send. */
.record-create__divider {
    height: 1px;
    margin: 6px 4px;
    background: var(--border-subtle, #e4e8ef);
}

/* -----------------------------------------------------------------------------
   HEADER CHOICE

   A question the header asks before it acts, when acting means picking between
   two things that are not interchangeable - ringing a person or ringing their
   company. It hangs under the button that raised it rather than opening a
   dialog: the answer is one click, and a dialog for one click is a ceremony.
   -------------------------------------------------------------------------- */

.header-choice-group {
    position: relative;
    flex: none;
    display: flex;
}

.header-choice {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 2;
    min-width: 260px;
    padding: 10px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
    box-shadow: var(--shadow-dropdown);
    text-align: left;
}

.header-choice__question {
    margin: 0 0 6px;
    padding: 0 6px;
    color: var(--text-muted);
    font-size: var(--font-size-tiny);
}

.header-choice__item {
    display: flex;
    flex-direction: column;
    gap: 1px;
    width: 100%;
    padding: 7px 6px;
    border: none;
    border-radius: var(--radius-small);
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.header-choice__item:hover {
    background: var(--surface-subtle);
}

.header-choice__label {
    color: var(--text-primary);
    font-size: var(--font-size-base);
    font-weight: 500;
}

.header-choice__detail {
    color: var(--text-muted);
    font-size: var(--font-size-small);
}

@media (max-width: 720px) {
    .header-choice {
        min-width: 0;
        width: max(220px, 60vw);
    }
}

@media (max-width: 720px) {
    .slide-over__header-actions {
        gap: 2px;
        margin-right: 0;
    }

    .slide-over__header-action {
        width: 30px;
        height: 30px;
    }
}

/* The body is one white sheet, and its sections are separated by ruled lines
   rather than by floating cards on a tint. Stacking cards inside a panel that
   is itself a card sitting over a scrim put three layers of surface between
   the reader and the content, and the panel already reads as a panel without
   any of them.

   `container-type` is what lets the layout below respond to the panel's own
   width rather than the window's. That matters because the panel is resizable:
   dragging it narrower has to collapse the two columns even though the window
   has not changed size at all, and a media query cannot see that. */
.slide-over__body {
    flex: 1;
    overflow-y: auto;
    padding: var(--gutter-tight);
    background: var(--surface-panel);
    container-type: inline-size;
}

/* -----------------------------------------------------------------------------
   TWO COLUMN PANEL LAYOUT

   Column one holds the identity of the record, column two everything that is
   done to it. Column one is the narrower of the two.
   ----------------------------------------------------------------------------- */

.slide-over__layout {
    display: grid;
    grid-template-columns: minmax(0, 0.88fr) minmax(0, 1.12fr);

    /* No gap: the columns are divided by a ruled line instead, and a line
       floating in the middle of a gap belongs to neither side. The breathing
       room comes from padding either side of the rule. */
    gap: 0;

    /* Stretched rather than start-aligned so the rule runs the full height of
       the taller column. A divider that stops halfway down looks like the page
       failed to finish rendering. */
    align-items: stretch;
}

.slide-over__column {
    min-width: 0;
    padding-right: var(--gutter-tight);
}

.slide-over__column + .slide-over__column {
    padding-right: 0;
    padding-left: var(--gutter-tight);
    border-left: var(--divider-width) solid var(--divider-colour);
}

/* Dragged narrow enough that two columns would be cramped - stack them, and
   turn the rule between them on its side rather than dropping it. */
@container (max-width: 820px) {
    .slide-over__layout {
        grid-template-columns: minmax(0, 1fr);
    }

    .slide-over__column {
        padding-right: 0;
    }

    .slide-over__column + .slide-over__column {
        padding-left: 0;
        padding-top: var(--gutter-tight);
        border-left: none;
        border-top: var(--divider-width) solid var(--divider-colour);
    }
}

.slide-over__footer {
    flex: none;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px var(--gutter);
    border-top: 1px solid var(--border-default);
    background: var(--surface-panel);
}

.slide-over__footer-spacer {
    flex: 1;
}


/* =============================================================================
   FORMS
   ============================================================================= */

/* A ruled band rather than a card, so the form still reads as distinct steps
   without an outline and a shadow around each one. The rule spans the section
   rather than the whole panel because it belongs to the content, not to the
   panel edge - and an inset line sits better under a heading that is inset
   too. */
.form-section {
    padding: 20px 0;
    container-type: inline-size;
    border-bottom: var(--divider-width) solid var(--divider-colour);
}

/* The panel's own padding already provides the space above the first section
   and below the last, and a trailing rule under the final section would draw
   a line under nothing. */
.form-section:first-child {
    padding-top: 0;
}

.form-section:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.form-section__header {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 18px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--border-subtle);
}

.form-section__badge {
    width: 34px;
    height: 34px;
    border-radius: var(--radius-medium);
    flex: none;
    display: grid;
    place-items: center;
    background: var(--accent-tint);
    color: var(--accent);
}

.form-section__heading-text {
    flex: 1;
    min-width: 0;
}

.form-section__title {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--text-primary);
}

.form-section__description {
    font-size: 12.5px;
    color: var(--text-muted);
    margin-top: 3px;
    line-height: 1.55;
}

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

/* At 760px there is room for three columns of short fields, which keeps the
   address block from running down the panel like a receipt. */
.form-grid--three-column {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.form-field--full-width {
    grid-column: 1 / -1;
}

.form-field--span-two {
    grid-column: span 2;
}

/* A section narrow enough that side-by-side fields would truncate their labels
   drops to a single column, independently of what the rest of the panel does. */
@container (max-width: 430px) {
    .form-grid,
    .form-grid--three-column {
        grid-template-columns: minmax(0, 1fr);
    }

    .form-field--span-two {
        grid-column: span 1;
    }

    .avatar-uploader {
        flex-direction: column;
        align-items: flex-start;
    }
}


/* =============================================================================
   AVATAR UPLOADER
   ============================================================================= */

.avatar-uploader {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 16px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-large);
    background: var(--surface-subtle);
    margin-bottom: 4px;
}

.avatar-uploader__preview {
    width: 76px;
    height: 76px;
    border-radius: var(--radius-button);
    flex: none;
    position: relative;
    display: grid;
    place-items: center;
    background: var(--brand-gradient);
    color: var(--text-on-dark);
    font-size: 24px;
    font-weight: 700;
    letter-spacing: 0.02em;
    overflow: hidden;
    box-shadow: 0 10px 26px -10px rgba(124, 92, 255, 0.8);
    cursor: pointer;
}

.avatar-uploader__preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.avatar-uploader__overlay {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    background: rgba(10, 12, 16, 0.55);
    color: var(--text-on-dark);
    opacity: 0;
    transition: opacity var(--duration-instant) var(--easing-standard);
}

.avatar-uploader__preview:hover .avatar-uploader__overlay {
    opacity: 1;
}

.avatar-uploader__details {
    flex: 1;
    min-width: 0;
}

.avatar-uploader__title {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.avatar-uploader__hint {
    font-size: 11.5px;
    color: var(--text-muted);
    margin-top: 3px;
    line-height: 1.5;
}

.avatar-uploader__status {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    margin-top: 7px;
    color: var(--text-muted);
    line-height: 1.45;
}

.avatar-uploader__status--success { color: var(--status-success); }
.avatar-uploader__status--error   { color: var(--status-danger); }
.avatar-uploader__status--working { color: var(--accent); }

/* The refresh glyph spins while an upload is in flight. */
.avatar-uploader__status--working .icon {
    animation: spinnerRotate 900ms linear infinite;
}

.avatar-uploader__preview--busy {
    opacity: 0.6;
    pointer-events: none;
}

.avatar-uploader__buttons {
    display: flex;
    gap: 8px;
    margin-top: 11px;
}

.avatar-uploader__file-input {
    display: none;
}

.form-field__label {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 6px;
}

.form-field__required-marker {
    color: var(--status-danger);
    margin-left: 2px;
}

/* -----------------------------------------------------------------------------
   UNDERLINE FIELDS

   A label-free field: the placeholder names it and a single line sits under
   it, no box. Used where a form wants to read as clean rows rather than a
   grid of outlined controls (compose, new invoice). Reusable across features.
   ----------------------------------------------------------------------------- */
.underline-field {
    margin-bottom: 14px;
}

.underline-input {
    width: 100%;
    border: none;
    border-bottom: 1px solid var(--border-default);
    border-radius: 0;
    background: transparent;
    padding: 8px 2px;
    font-size: 14px;
    font-family: inherit;
    color: var(--text-primary);
    transition: border-color 0.12s ease;
    -webkit-appearance: none;
    appearance: none;
}

.underline-input:focus {
    outline: none;
    border-bottom-color: var(--accent, #2f6bff);
}

.underline-input::placeholder {
    color: var(--text-muted);
}

.underline-input:disabled {
    color: var(--text-muted);
    cursor: default;
}

select.underline-input {
    background-image:
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%238b929b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
    background-repeat: no-repeat;
    background-position: right 4px center;
    padding-right: 20px;
    cursor: pointer;
}

.form-field__control {
    width: 100%;
    height: var(--input-height);
    padding: 0 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
    font: inherit;
    font-size: var(--font-size-base);
    color: var(--text-primary);
    outline: 0;
    transition:
        border-color var(--duration-instant) var(--easing-standard),
        box-shadow   var(--duration-instant) var(--easing-standard);
}

.form-field__control::placeholder {
    color: var(--text-muted);
}

.form-field__control:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-tint);
}

.form-field__control:disabled {
    background: var(--surface-subtle);
    color: var(--text-muted);
    cursor: not-allowed;
}

.form-field__control--invalid {
    border-color: var(--status-danger);
}

.form-field__control--invalid:focus {
    box-shadow: 0 0 0 3px var(--status-danger-tint);
}

select.form-field__control {
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238b929b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 11px center;
    padding-right: 34px;
}

.form-field__hint {
    font-size: 11.5px;
    color: var(--text-muted);
    margin-top: 5px;
    line-height: 1.5;
}

.form-field__error {
    font-size: 11.5px;
    color: var(--status-danger);
    margin-top: 5px;
    font-weight: 500;
}

.form-field__readonly-value {
    display: flex;
    align-items: center;
    height: var(--input-height);
    padding: 0 12px;
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-medium);
    background: var(--surface-subtle);
    font-family: var(--font-monospace);
    font-size: 13px;
    color: var(--text-muted);
}

.form-checkbox {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    cursor: pointer;
}

.form-checkbox input {
    width: 17px;
    height: 17px;
    margin-top: 1px;
    accent-color: var(--accent);
    cursor: pointer;
    flex: none;
}

.form-checkbox__text {
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.5;
}

.form-checkbox__description {
    display: block;
    font-size: 11.5px;
    color: var(--text-muted);
    margin-top: 2px;
}

.password-field {
    position: relative;
}

.password-field__reveal-button {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 30px;
    height: 30px;
    border-radius: var(--radius-button);
    display: grid;
    place-items: center;
    color: var(--text-muted);
    transition:
        background var(--duration-instant) var(--easing-standard),
        color      var(--duration-instant) var(--easing-standard);
}

.password-field__reveal-button:hover {
    background: var(--border-subtle);
    color: var(--text-primary);
}

.password-field .form-field__control {
    padding-right: 42px;
}

.password-strength-meter {
    display: flex;
    gap: 4px;
    margin-top: 8px;
}

.password-strength-meter__segment {
    flex: 1;
    height: 3px;
    border-radius: 2px;
    background: var(--border-default);
    transition: background var(--duration-quick) var(--easing-standard);
}

.password-strength-meter__segment--weak    { background: var(--status-danger); }
.password-strength-meter__segment--fair    { background: var(--status-warning); }
.password-strength-meter__segment--strong  { background: var(--status-success); }


/* =============================================================================
   PERMISSION TREE

   688 checkboxes need to feel like a document rather than a wall. The grid is
   fixed so every action column lines up down the whole tree, and indentation
   carries the hierarchy.
   ============================================================================= */

.permission-tree__toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding-bottom: 12px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--border-subtle);
}

.permission-tree__count {
    margin-left: auto;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
}

.permission-tree {
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    overflow: hidden;
}

.permission-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) repeat(4, 62px);
    align-items: center;
    min-height: 34px;
    border-bottom: 1px solid var(--border-subtle);
    transition: background var(--duration-instant) var(--easing-standard);
}

.permission-row:last-child {
    border-bottom: 0;
}

.permission-row:hover:not(.permission-row--header) {
    background: var(--surface-subtle);
}

/* The action column headings stay put while the tree scrolls. */
.permission-row--header {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--surface-subtle);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-muted);
    min-height: 30px;
}

.permission-row__label {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    min-width: 0;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.permission-row__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.permission-row__action {
    display: grid;
    place-items: center;
    text-align: center;
}

.permission-row__action input[type="checkbox"] {
    width: 15px;
    height: 15px;
    accent-color: var(--accent);
    cursor: pointer;
}

/* Depth carries the hierarchy; a module reads as a heading, a page as a leaf. */
.permission-row--depth-1 .permission-row__label {
    padding-left: 10px;
    font-weight: 700;
    font-size: var(--font-size-base);
    color: var(--text-primary);
}

.permission-row--depth-1 {
    background: var(--surface-subtle);
}

.permission-row--depth-2 .permission-row__label {
    padding-left: 28px;
    font-weight: 600;
    color: var(--text-primary);
}

.permission-row--depth-3 .permission-row__label {
    padding-left: 52px;
}

.permission-row__toggle {
    width: 18px;
    height: 18px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 4px;
    color: var(--text-muted);
    transition:
        transform  var(--duration-instant) var(--easing-standard),
        background var(--duration-instant) var(--easing-standard);
}

.permission-row__toggle:hover {
    background: var(--border-default);
    color: var(--text-primary);
}

.permission-row__toggle--open {
    transform: rotate(90deg);
}

.permission-row__toggle-spacer {
    width: 18px;
    flex: none;
}

/* Survives collapsing: tells you a module has grants without expanding it. */
.permission-row__branch-count {
    margin-left: auto;
    padding-left: 8px;
    font-size: 10.5px;
    font-weight: 600;
    color: var(--accent);
    white-space: nowrap;
}

.permission-standalone {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--border-subtle);
}

.permission-standalone__title {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--text-primary);
}

.permission-standalone__description {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    margin: 4px 0 12px;
    line-height: 1.55;
}

.permission-action-row {
    padding: 8px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.permission-action-row:last-child {
    border-bottom: 0;
}


/* =============================================================================
   ROLE PICKER - the checkbox list on the user form
   ============================================================================= */

.role-picker {
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-medium);
    max-height: 220px;
    overflow-y: auto;
}

.role-picker__option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-subtle);
    cursor: pointer;
    transition: background var(--duration-instant) var(--easing-standard);
}

.role-picker__option:last-child {
    border-bottom: 0;
}

.role-picker__option:hover {
    background: var(--surface-subtle);
}

.role-picker__option input {
    width: 16px;
    height: 16px;
    margin-top: 1px;
    accent-color: var(--accent);
    cursor: pointer;
    flex: none;
}

.role-picker__name {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--text-primary);
}

.role-picker__description {
    display: block;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    margin-top: 2px;
    line-height: 1.5;
}

.role-picker__empty {
    padding: 18px;
    text-align: center;
    font-size: var(--font-size-small);
    color: var(--text-muted);
}


/* =============================================================================
   SECURITY ACTIONS AND TWO FACTOR ENROLMENT
   ============================================================================= */

.action-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.action-row:last-child {
    border-bottom: 0;
    padding-bottom: 0;
}

.action-row__text {
    flex: 1;
    min-width: 0;
}

.action-row__title {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text-primary);
}

.action-row__description {
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 3px;
    line-height: 1.5;
}

.two-factor-enrolment {
    margin-top: 14px;
    padding: 18px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-large);
    background: var(--surface-subtle);
}

.two-factor-enrolment__qr {
    width: 168px;
    height: 168px;
    margin: 0 auto 14px;
    background: var(--surface-panel);
    border-radius: var(--radius-medium);
    padding: 8px;
    border: 1px solid var(--border-default);
    display: grid;
    place-items: center;
}

.two-factor-enrolment__qr svg,
.two-factor-enrolment__qr img {
    width: 100%;
    height: 100%;
}

.two-factor-enrolment__secret {
    font-family: var(--font-monospace);
    font-size: 12.5px;
    text-align: center;
    color: var(--text-secondary);
    background: var(--surface-panel);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-small);
    padding: 8px 10px;
    margin-bottom: 14px;
    word-break: break-all;
}

.two-factor-enrolment__instruction {
    font-size: 12.5px;
    color: var(--text-muted);
    line-height: 1.6;
    margin-bottom: 14px;
    text-align: center;
}

.verification-code-row {
    display: flex;
    gap: 10px;
    align-items: flex-start;
}

.verification-code-row .form-field__control {
    font-family: var(--font-monospace);
    letter-spacing: 0.3em;
    text-align: center;
    font-size: 15px;
}


/* =============================================================================
   INLINE NOTICES
   ============================================================================= */

.inline-notice {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--radius-medium);
    font-size: 12.5px;
    line-height: 1.55;
    margin-bottom: 16px;
}

.inline-notice__icon {
    flex: none;
    margin-top: 1px;
}

.inline-notice--error   { background: var(--status-danger-tint);  color: #8a231a; }
.inline-notice--success { background: var(--status-success-tint); color: #0a6b3d; }
.inline-notice--info    { background: var(--status-info-tint);    color: #15489f; }
.inline-notice--warning { background: var(--status-warning-tint); color: #8a5300; }

.inline-notice code {
    font-family: var(--font-monospace);
    font-size: 11.5px;
    word-break: break-all;
}


/* =============================================================================
   TOAST NOTIFICATIONS
   ============================================================================= */

.toast-stack {
    position: fixed;
    bottom: 22px;
    right: 22px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 200;
    pointer-events: none;
}

@keyframes toastEnter {
    from { opacity: 0; transform: translateY(12px) scale(0.97); }
    to   { opacity: 1; transform: none; }
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    min-width: 280px;
    max-width: 400px;
    padding: 13px 16px;
    border-radius: var(--radius-medium);
    background: #16181d;
    color: var(--text-on-dark);
    font-size: 13px;
    line-height: 1.5;
    box-shadow: var(--shadow-dropdown);
    pointer-events: auto;
    animation: toastEnter var(--duration-quick) var(--easing-standard) both;
}

.toast--leaving {
    opacity: 0;
    transform: translateY(8px);
    transition:
        opacity   var(--duration-quick) var(--easing-standard),
        transform var(--duration-quick) var(--easing-standard);
}

.toast__icon         { flex: none; margin-top: 1px; }
.toast--success .toast__icon { color: #4ade80; }
.toast--error   .toast__icon { color: #fb7185; }
.toast--info    .toast__icon { color: #60a5fa; }


/* =============================================================================
   SIGN-IN SCREEN
   ============================================================================= */

.sign-in-screen {
    position: fixed;
    inset: 0;
    background: var(--surface-header);
    display: grid;
    place-items: center;
    z-index: 300;
    padding: 24px;
}

.sign-in-card {
    width: 100%;
    max-width: 400px;
}

.sign-in-card__brand {
    display: flex;
    justify-content: center;
    margin-bottom: 30px;
}

.sign-in-card__brand .brand-mark {
    font-size: 26px;
}

.sign-in-card__panel {
    background: var(--surface-panel);
    border-radius: var(--radius-large);
    padding: 28px;
    box-shadow: 0 24px 60px -16px rgba(0, 0, 0, 0.6);
}

.sign-in-card__title {
    font-size: 19px;
    font-weight: 600;
    letter-spacing: -0.02em;
    margin-bottom: 5px;
}

.sign-in-card__description {
    font-size: 13px;
    color: var(--text-muted);
    margin-bottom: 22px;
    line-height: 1.55;
}

.sign-in-card__fields {
    display: flex;
    flex-direction: column;
    gap: 15px;
    margin-bottom: 20px;
}

.sign-in-card__footnote {
    text-align: center;
    font-size: 11.5px;
    color: var(--text-on-dark-muted);
    margin-top: 20px;
    line-height: 1.6;
}


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

@media (max-width: 900px) {
    /* Below this width there is nothing to drag the panel away from, so it
       takes the screen and the grip is removed rather than left inert. */
    .slide-over {
        width: 100vw !important;
        min-width: 0;
        max-width: 100vw;
    }

    .slide-over__resize-handle { display: none; }

    /* On a phone the footer is the last thing on a long scroll and easy to miss.
       Pinning it above the safe area keeps Save reachable at all times, and the
       inset padding keeps it clear of the home indicator on an iPhone. */
    .slide-over__footer {
        padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    }

    .slide-over__body {
        padding: 12px;
        /* Room to scroll past the pinned footer. */
        padding-bottom: 24px;
        -webkit-overflow-scrolling: touch;
    }

    .slide-over__title    { font-size: var(--font-size-base); }
    .slide-over__subtitle { font-size: var(--font-size-tiny); }

    .slide-over__identity { width: 38px; height: 38px; border-radius: var(--radius-button); font-size: 13px; }

    .page-header {
        flex-direction: column;
        align-items: stretch;
        gap: 12px;
    }

    .page-header__actions .button { flex: 1; }

    .filter-field { width: 100%; }

    .record-count { margin-left: 0; }

    .avatar-uploader__preview { width: 62px; height: 62px; border-radius: var(--radius-button); font-size: 20px; }

    .toast-stack { left: 12px; right: 12px; bottom: 12px; }
    .toast       { min-width: 0; max-width: none; }
}

@media (max-width: 560px) {
    /* A six column table cannot be made to fit a phone, so the columns that
       repeat information available elsewhere are dropped rather than squeezed:
       the reference and job title already appear under the person's name. */
    .data-table thead th:nth-child(1),
    .data-table tbody td:nth-child(1),
    .data-table thead th:nth-child(3),
    .data-table tbody td:nth-child(3),
    .data-table thead th:nth-child(6),
    .data-table tbody td:nth-child(6) {
        display: none;
    }

    .form-section        { padding: 14px 14px 16px; }
    .form-section__badge { width: 30px; height: 30px; }

    /* Cancel and Save share the width evenly rather than hugging the edges,
       which puts both inside comfortable thumb reach. */
    .slide-over__footer         { gap: 8px; }
    .slide-over__footer .button { flex: 1; padding: 0 10px; }
    .slide-over__footer-spacer  { display: none; }

    .sign-in-card__panel { padding: 20px; }

    /* iOS zooms the page in whenever a focused input is under 16px. Sizing the
       controls at exactly 16px on phones prevents that jump without making the
       rest of the interface larger. */
    .form-field__control,
    .filter-field__input,
    .global-search__input {
        font-size: 16px;
    }

    .action-row {
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
    }

    .action-row .button { width: 100%; }
}


/* =============================================================================
   PHONE - PAGE FURNITURE

   A page toolbar is a single row on a desktop: filter, count, then actions
   pushed right. At 390px that row is about twice the available width, and
   because it does not wrap it made the page wider than the screen rather than
   taller. Wrapping is the whole fix; the rest is making the wrapped result
   worth looking at.
   ============================================================================= */

@media (max-width: 560px) {

    .page-surface {
        min-width: 0;
    }

    .page-toolbar {
        flex-wrap: wrap;
        gap: 8px;
    }

    /* The filter takes its own row. Sharing one with two buttons leaves it too
       narrow to show what has been typed into it. */
    .page-toolbar .filter-field {
        flex: 1 1 100%;
        min-width: 0;
    }

    /* Buttons split the row beneath it evenly, which is both reachable by thumb
       and honest about there being two of them. */
    .page-toolbar .button {
        flex: 1 1 0;
        min-width: 0;
        justify-content: center;
    }

    /* The record count sits with the filter rather than claiming a row. */
    .record-count {
        flex: none;
    }

    /* A table wider than the screen scrolls inside its own box. It must never
       widen the page - that is what dragged the header off screen. */
    .data-table-scroll {
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* Full-bleed pages keep a little breathing room from the screen edge at
       this size, where text otherwise runs into the bezel. */
    .page-header {
        padding-left: var(--gutter);
        padding-right: var(--gutter);
    }
}


/* =============================================================================
   TABLET AND BELOW - THE PAGE GROWS, THE STAGE SCROLLS

   Paired with the `.content-stage` rule in application-shell.css. A page is a
   fixed-height flex column on a desktop, with one scrolling region inside it.
   Below 900px that inverts: the page is as tall as its content and the stage
   around it scrolls.

   Every scrolling region inside a page therefore has to stop claiming the
   leftover height, or it competes with the stage for the same scroll and one
   of them wins by being zero pixels tall.
   ============================================================================= */

@media (max-width: 900px) {

    .page-surface {
        height: auto;
        min-height: 100%;
        overflow: visible;
    }

    /* `overflow-y: visible` alongside an `overflow-x: auto` computes back to
       auto - which is harmless here, because these are now content-height and
       so have nothing to scroll vertically. What matters is that a table wider
       than the screen still scrolls sideways within its own box. */
    .data-table-scroll,
    .app-catalogue-scroll,
    .website-list-scroll {
        flex: none;
        min-height: 0;
        overflow-y: visible;
    }

    /* The page body hands its scroll back to the stage, but keeps its gutter -
       that padding is what holds the content off the edge of a phone screen. */
    .page-scroll {
        flex: none;
        overflow-y: visible;
    }
}


/* =============================================================================
   SECURITY TABLES
   ============================================================================= */

/* A pill sitting inline with a name rather than in its own column - "this
   device", "super admin". It qualifies the name, so it travels with it. */
.session-badge {
    margin-left: 8px;
    vertical-align: middle;
}

/* A user agent read into something human. The full string is on the title
   attribute, because the parsed version is a best guess and anybody checking
   whether a session is really theirs deserves the original. */
.session-device {
    color: var(--text-primary);
    cursor: help;
}


/* =============================================================================
   REPEATING CONTACT ROWS

   A label, a value, a "main" marker and a way to remove it. The value takes
   whatever space is left, because it is the only part that varies in length.
   ============================================================================= */

.contact-method-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.contact-method-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.contact-method-row__label {
    flex: 0 0 116px;
    min-width: 0;
}

.contact-method-row__value {
    flex: 1 1 auto;
    min-width: 0;
}

.contact-method-row__primary {
    flex: none;
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    cursor: pointer;
    user-select: none;
}

.contact-method-row__primary input {
    accent-color: var(--accent);
    cursor: pointer;
}

.contact-method-row__remove {
    flex: none;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-small);
    display: grid;
    place-items: center;
    color: var(--text-muted);
    background: none;
    border: 0;
    cursor: pointer;
    transition:
        background var(--duration-instant) var(--easing-standard),
        color      var(--duration-instant) var(--easing-standard);
}

.contact-method-row__remove:hover {
    background: var(--status-danger-tint);
    color: var(--status-danger);
}

.contact-method-add {
    margin-top: 10px;
}


/* =============================================================================
   ATTACHMENTS
   ============================================================================= */

.attachment-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 12px;
}

.attachment-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.attachment-row:last-child {
    border-bottom: none;
}

.attachment-row__icon {
    flex: none;
    display: flex;
    color: var(--text-muted);
}

.attachment-row__detail {
    flex: 1;
    min-width: 0;
}

.attachment-row__name {
    display: block;
    font-size: var(--font-size-base);
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.attachment-row__meta {
    display: block;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

/* On a phone the label, value, marker and remove button cannot share a line
   without the value shrinking to nothing. The value takes its own row and the
   controls sit under it. */
@media (max-width: 560px) {
    .contact-method-row {
        flex-wrap: wrap;
    }

    .contact-method-row__value {
        flex: 1 1 100%;
        order: -1;
    }

    .contact-method-row__label {
        flex: 1 1 auto;
    }
}


/* =============================================================================
   COMPACT PANEL FORMS

   Panel forms lost their field labels, so the boxes carry their own names as
   placeholders and no longer need room above them. Without that line a
   40px-tall control looks oversized for the text in it, and a long form runs
   off the bottom of a laptop before the first section ends.

   Scoped to `.form-section` rather than applied to every control in the
   product: the authentication screen and the search field are not forms and
   have no label problem to solve.
   ============================================================================= */

.form-section .form-field__control {
    height: 34px;
    padding-top: 0;
    padding-bottom: 0;
}

/* A textarea has no fixed height to shrink and a date input needs its label
   back, so neither is caught by the rule above. */
.form-section textarea.form-field__control {
    height: auto;
}

.form-section .form-grid {
    gap: 10px;
}

/* A checkbox that reveals a section. Sits directly under the section heading
   with the fields it controls beneath it, so the relationship is positional
   rather than something to be explained. */
.form-checkbox {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 4px 0 12px;
    font-size: var(--font-size-base);
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
}

.form-checkbox input {
    width: 16px;
    height: 16px;
    accent-color: var(--accent);
    cursor: pointer;
}

/* A field nobody may edit - the client number. Greyed rather than disabled, so
   it can still be selected and copied. */
.form-section .form-field__control[readonly] {
    background: var(--surface-subtle);
    color: var(--text-muted);
    cursor: default;
}

/* The contact rows sit in a form section too, so they follow the same height. */
.contact-method-row .form-field__control {
    height: 34px;
}


/* =============================================================================
   SYSTEM DIALOGS

   The product's own confirm and prompt - a small centred card over a scrim,
   above everything including the slide-overs. Native browser dialogs are
   banned: they block the tab, ignore the theme, and cannot be worded well.
   ============================================================================= */

.system-dialog-scrim {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: grid;
    place-items: center;
    padding: 20px;
    background: rgba(15, 17, 21, 0.45);
    animation: systemDialogFadeIn var(--duration-instant) var(--easing-standard);
}

.system-dialog {
    width: 100%;
    max-width: 420px;
    padding: 20px 22px;
    border-radius: var(--radius-large);
    background: var(--surface-panel);
    box-shadow: var(--shadow-dropdown);
    animation: systemDialogRise var(--duration-quick) var(--easing-standard);
}

.system-dialog__title {
    margin: 0 0 6px;
    font-size: var(--font-size-medium);
    font-weight: 700;
    color: var(--text-primary);
}

.system-dialog__message {
    margin: 0;
    font-size: var(--font-size-base);
    color: var(--text-secondary);
    line-height: 1.55;
}

.system-dialog__detail {
    margin: 8px 0 0;
    font-size: var(--font-size-small);
    color: var(--text-muted);
    line-height: 1.5;
    /* A blank line in the text stays a blank line. A dialog that has to list
       consequences - what a delete keeps and what it destroys - reads as a
       wall otherwise, and this is the one place people actually read. */
    white-space: pre-line;
}

.system-dialog__field {
    margin-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.system-dialog__actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 18px;
}

.system-dialog__confirm--danger {
    background: var(--status-danger);
    border-color: var(--status-danger);
}

.system-dialog__confirm--danger:hover {
    background: #b8261b;
    border-color: #b8261b;
}

@keyframes systemDialogFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes systemDialogRise {
    from { opacity: 0; transform: translateY(10px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}


/* =============================================================================
   LIST PAGES - Active/Archived tabs, multi-select, bulk action bar
   (shared by Invoices, Subscriptions, Projects, Tasks)
   ============================================================================= */

.list-view-tabs,
.record-tabs.list-view-tabs {
    margin: 4px 0 12px;
    padding: 0 var(--gutter);
}

.invoice-toolbar .list-bulk-bar { display: none; }
.list-bulk-bar--on {
    display: flex;
    align-items: center;
    gap: 14px;
    order: -1;
    margin-right: auto;
    flex-wrap: wrap;
}
.list-bulk-bar__count { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.list-bulk-bar__actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.list-table__check { width: 42px; text-align: center; padding-left: 6px; padding-right: 6px; }
.list-check {
    width: 16px; height: 16px;
    cursor: pointer;
    accent-color: var(--accent, #6366f1);
    vertical-align: middle;
}
.list-row--selected { background: var(--surface-hover, rgba(99, 102, 241, 0.06)); }

/* The bar as a row of its own, for lists whose toolbar is not the flex
   container it was written to slot into (Clients, Organisations). Empty it
   occupies nothing; only `--on` gives it a line.

   `order: -1` and `margin-right: auto` are undone deliberately: they exist to
   push the bar to the front of a flex TOOLBAR, and a page surface is also a
   flex column - so inherited they threw the bar above the page title, where it
   read as part of the chrome rather than as something about the rows below. */
.page-surface > .list-bulk-bar--on {
    order: 0;
    margin: 0 var(--gutter) 12px;
    padding: 8px 14px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
}

/* Pipeline progress bar (Projects table) */
.projects-progress-col { width: 130px; }
.projects-progress {
    display: flex;
    align-items: center;
    gap: 8px;
}
.projects-progress__track {
    flex: 1 1 auto;
    height: 6px;
    min-width: 60px;
    border-radius: 999px;
    background: var(--border-subtle, #e6e9ef);
    overflow: hidden;
}
.projects-progress__track > span {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: #2f6fed;
    transition: width 0.3s ease;
}
.projects-progress__pct {
    flex: none;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted, #6b7280);
    min-width: 30px;
    text-align: right;
}

/* New Project slide-over form */
.project-new-form { display: flex; flex-direction: column; gap: 14px; }
.project-new-form__row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
.project-new-form .form-field { margin: 0; }

/* Project Settings lists (statuses / sources / types) */
.settings-swatch {
    display: inline-block;
    width: 14px; height: 14px;
    border-radius: 4px;
    vertical-align: middle;
    margin-right: 8px;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}
.settings-swatch__hex { color: var(--text-muted, #6b7280); font-size: 12px; }
.settings-tag {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted, #6b7280);
    background: var(--surface-hover, #f1f3f7);
    border-radius: var(--radius-button);
    padding: 1px 8px;
    margin-left: 6px;
    vertical-align: middle;
}
.settings-row__actions { white-space: nowrap; text-align: right; }
.settings-row__actions .button--small { padding: 4px 6px; }
.settings-check {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-primary);
    cursor: pointer;
}
.settings-check input { width: 16px; height: 16px; accent-color: var(--accent, #6366f1); }
.settings-colour { display: flex; align-items: center; gap: 10px; }
.settings-colour input[type="color"] {
    width: 42px; height: 30px;
    border: 1px solid var(--border-default, #d7dbe3);
    border-radius: var(--radius-button);
    padding: 2px;
    background: #fff;
    cursor: pointer;
}
.settings-colour__hint { color: var(--text-muted, #6b7280); font-size: 12px; }
.record-subtle { color: var(--text-muted, #6b7280); font-weight: 400; }

/* Organisation panel form (new + edit) */
.org-form { display: flex; flex-direction: column; gap: 16px; }
.org-form__section { display: flex; flex-direction: column; gap: 12px; }
.org-form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.org-form__row .form-field { margin: 0; }
.org-form__subhead {
    margin: 6px 0 2px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted, #6b7280);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.org-method-list { display: flex; flex-direction: column; gap: 8px; }
.contact-method-row { display: flex; align-items: center; gap: 8px; }
.contact-method-row__value { flex: 1 1 auto; }
.contact-method-row__primary {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 12px; color: var(--text-muted, #6b7280); white-space: nowrap;
}
.contact-method-row__primary input { width: 15px; height: 15px; accent-color: var(--accent, #6366f1); }
.contact-method-row__remove {
    flex: none; border: none; background: transparent; cursor: pointer;
    color: var(--text-muted, #6b7280); padding: 4px; border-radius: var(--radius-button); line-height: 0;
}
.contact-method-row__remove:hover { background: var(--surface-hover, #f1f3f7); color: var(--text-primary); }

.org-contact-results {
    display: none; flex-direction: column;
    border: 1px solid var(--border-default, #d7dbe3);
    border-radius: var(--radius-button); margin-top: 6px; overflow: hidden;
}
.org-contact-results.is-open { display: flex; }
.org-contact-results__item {
    text-align: left; border: none; background: #fff; cursor: pointer;
    padding: 8px 10px; font-size: 13px; color: var(--text-primary);
}
.org-contact-results__item:hover { background: var(--surface-hover, #f1f3f7); }
.org-contact-results__empty { padding: 8px 10px; font-size: 13px; color: var(--text-muted, #6b7280); }

.org-contact-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.org-chip {
    display: inline-flex; align-items: center; gap: 4px;
    background: var(--surface-hover, #f1f3f7); border-radius: var(--radius-button);
    padding: 3px 6px 3px 10px; font-size: 12px; color: var(--text-primary);
}
.org-chip button { border: none; background: transparent; cursor: pointer; color: var(--text-muted, #6b7280); padding: 2px; line-height: 0; border-radius: 999px; }
.org-chip button:hover { color: var(--status-danger, #dc2626); }

/* The organisation a client is linked to, on the client form */
.org-linked-value { display: none; }
.org-linked-value.is-set {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    background: var(--surface-hover, #f1f3f7); border-radius: var(--radius-button);
    padding: 8px 10px; margin-bottom: 4px;
}
.org-linked-value__name { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.org-linked-value__clear { border: none; background: transparent; cursor: pointer; color: var(--text-muted, #6b7280); padding: 2px; line-height: 0; }
.org-linked-value__clear:hover { color: var(--status-danger, #dc2626); }

.button--ghost {
    background: transparent;
    color: var(--text-muted);
    border: 1px solid transparent;
}
.button--ghost:hover:not(:disabled) {
    background: var(--surface-hover, #f5f7f9);
    color: var(--text-primary);
}


/* =============================================================================
   AUDIT LOG
   ============================================================================= */

/* The quick filters sit in the toolbar rather than above the table, so they
   read as "narrow this" rather than as page navigation. */
.audit-choice-group {
    display: flex;
    gap: 4px;
    border-bottom: none;
    flex-wrap: wrap;
}

.audit-choice-group .record-tab {
    padding: 6px 12px;
    font-size: 12.5px;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border-default);
    background: var(--surface-panel);
}

.audit-choice-group .record-tab--active {
    background: var(--surface-subtle);
    border-color: var(--accent-primary, #6366f1);
    color: var(--accent-primary, #6366f1);
    font-weight: 600;
}

/* A detail is a sentence, not a label - it wraps rather than stretching the
   table past the edge of the screen. */
.audit-detail {
    max-width: 380px;
    white-space: normal;
    line-height: 1.45;
    color: var(--text-secondary, #6b7280);
    font-size: 12.5px;
}

/* The locked-account notices carry an action, so they lay out as a row with
   the button pushed to the end. */
#auditLockBanner .inline-notice {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: 12px;
}

#auditLockBanner .inline-notice .button {
    margin-left: auto;
    flex-shrink: 0;
    align-self: center;
}



/* =============================================================================
   SETTINGS > PORTAL

   The General screen is a form beside a live preview, so it is a two-column
   layout that collapses to one on anything narrow. The Access screen is a list
   of switches - a real switch, not a checkbox, because these take capability
   away from people and the control should feel like it means something.
   ============================================================================= */

.portal-settings-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
    gap: 24px;
    align-items: start;
}

@media (max-width: 1100px) {
    .portal-settings-layout { grid-template-columns: minmax(0, 1fr); }
}

.portal-settings-preview { position: sticky; top: 16px; }

.portal-preview-label {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--colour-text-muted, #6b7280);
    margin-bottom: 8px;
}

.portal-preview {
    border: 1px solid var(--colour-border, #e5e7eb);
    border-radius: var(--radius-button);
    overflow: hidden;
    background: #fff;
    box-shadow: 0 8px 24px rgba(16, 24, 40, .07);
    margin-bottom: 10px;
}

.portal-preview__bar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 16px;
    color: #fff;
}

.portal-preview__logo { max-height: 26px; max-width: 150px; display: block; }
.portal-preview__wordmark { font-weight: 700; letter-spacing: -.3px; font-size: 15px; }
.portal-preview__spacer { flex: 1; }

.portal-preview__signout {
    font-size: 11px;
    background: rgba(255, 255, 255, .2);
    border-radius: 999px;
    padding: 4px 12px;
}

.portal-preview__tabs {
    display: flex;
    gap: 14px;
    padding: 0 16px;
    border-bottom: 1px solid var(--colour-border, #e5e7eb);
    overflow-x: auto;
}

.portal-preview__tab {
    font-size: 11px;
    padding: 10px 0;
    color: var(--colour-text-muted, #6b7280);
    border-bottom: 2px solid transparent;
    white-space: nowrap;
}

.portal-preview__tab--active { color: #111827; font-weight: 600; }

.portal-preview__body { padding: 16px; }
.portal-preview__greeting { font-size: 15px; font-weight: 650; }

.portal-preview__welcome {
    font-size: 12px;
    color: var(--colour-text-muted, #6b7280);
    margin-top: 4px;
    white-space: pre-wrap;
}

.portal-preview__button {
    display: inline-block;
    margin-top: 12px;
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    border-radius: var(--radius-button);
    padding: 7px 16px;
}

.portal-preview__footer {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: space-between;
    padding: 12px 16px;
    border-top: 1px solid var(--colour-border, #e5e7eb);
    font-size: 11px;
    color: var(--colour-text-muted, #6b7280);
    white-space: pre-wrap;
}

.portal-preview__contact { font-weight: 600; }

/* ---- colour and image fields ------------------------------------------- */

.portal-colour-row { display: flex; align-items: center; gap: 10px; }

.portal-colour-swatch {
    width: 44px;
    height: 38px;
    padding: 2px;
    border: 1px solid var(--colour-border, #e5e7eb);
    border-radius: var(--radius-button);
    background: #fff;
    cursor: pointer;
    flex-shrink: 0;
}

.portal-image-row {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 10px;
}

.portal-image-preview {
    width: 92px;
    height: 62px;
    border: 1px dashed var(--colour-border, #d1d5db);
    border-radius: var(--radius-button);
    display: flex;
    align-items: center;
    justify-content: center;
    background: #f9fafb;
    overflow: hidden;
    flex-shrink: 0;
}

.portal-image-preview img { max-width: 100%; max-height: 100%; display: block; }

.portal-image-preview__empty {
    font-size: 11px;
    color: var(--colour-text-muted, #9ca3af);
}

.portal-image-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.button--ghost {
    background: transparent;
    border: 1px solid var(--colour-border, #e5e7eb);
    color: var(--colour-text-muted, #6b7280);
}

.button--ghost:disabled { opacity: .5; cursor: not-allowed; }

.portal-save-row {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin: 4px 0 18px;
}

.portal-save-note {
    font-size: 12px;
    color: var(--colour-text-muted, #6b7280);
}


/* ---- the switches on Settings > Portal > Access -------------------------- */

.portal-switch-list { display: grid; gap: 4px; }

.portal-switch {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 14px 12px;
    border-radius: var(--radius-button);
    cursor: pointer;
    transition: background .12s;
}

.portal-switch:hover { background: #f9fafb; }
.portal-switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.portal-switch__track {
    position: relative;
    width: 40px;
    height: 23px;
    border-radius: 999px;
    background: #d1d5db;
    flex-shrink: 0;
    margin-top: 1px;
    transition: background .16s;
}

.portal-switch__thumb {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 17px;
    height: 17px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(16, 24, 40, .3);
    transition: transform .16s;
}

.portal-switch input:checked + .portal-switch__track { background: #12b76a; }

.portal-switch input:checked + .portal-switch__track .portal-switch__thumb {
    transform: translateX(17px);
}

.portal-switch input:focus-visible + .portal-switch__track {
    outline: 2px solid #2563eb;
    outline-offset: 2px;
}

.portal-switch input:disabled + .portal-switch__track { opacity: .5; }
.portal-switch:has(input:disabled) { cursor: default; }
.portal-switch:has(input:disabled):hover { background: transparent; }

.portal-switch__text { display: flex; flex-direction: column; gap: 3px; }
.portal-switch__label { font-size: 14px; font-weight: 600; }

.portal-switch__note {
    font-size: 12.5px;
    color: var(--colour-text-muted, #6b7280);
    line-height: 1.5;
}

/* ---- the account tiles on Settings > Portal > Logs ----------------------- */

.portal-summary-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}

.portal-summary-row:empty { display: none; }

.portal-summary-tile {
    border: 1px solid var(--colour-border, #e5e7eb);
    border-radius: var(--radius-button);
    padding: 14px 16px;
    background: #fff;
}

.portal-summary-tile__value { font-size: 22px; font-weight: 680; letter-spacing: -.5px; }

.portal-summary-tile__label {
    font-size: 12px;
    color: var(--colour-text-muted, #6b7280);
    margin-top: 2px;
}


/* =============================================================================
   SETTINGS > CHECKOUTS

   Reuses the two-column form/preview layout from Settings > Portal. The right
   column here is an example quote rather than a visual preview, because the
   thing that is hard to picture about a catalogue is not what it looks like -
   it is what it adds up to.
   ============================================================================= */

.checkout-example,
.checkout-basket {
    border: 1px solid var(--colour-border, #e5e7eb);
    border-radius: 14px;
    background: #fff;
    padding: 16px;
    box-shadow: 0 8px 24px rgba(16, 24, 40, .07);
}

.checkout-example__row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px solid #f2f3f5;
    font-size: 13px;
}

.checkout-example__row:last-of-type { border-bottom: none; }
.checkout-example__label { color: var(--colour-text-muted, #6b7280); }
.checkout-example__value { font-weight: 650; }

.checkout-basket__controls {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    padding-bottom: 12px;
    margin-bottom: 10px;
    border-bottom: 1px solid var(--colour-border, #e5e7eb);
}

.checkout-basket__control { flex: 1 1 110px; }

.checkout-basket__line {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 7px 0;
    font-size: 13px;
}

.checkout-basket__name { color: #374151; }
.checkout-basket__price { font-variant-numeric: tabular-nums; white-space: nowrap; }

.checkout-basket__total {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 0 0;
    margin-top: 8px;
    border-top: 1px solid var(--colour-border, #e5e7eb);
    font-size: 14px;
}

.checkout-basket__total strong { font-variant-numeric: tabular-nums; }

/* ---- page tiers ---------------------------------------------------------- */

.checkout-tiers { display: grid; gap: 10px; }

/* The page count is written into the field's own outline now, so the row is a
   field and a button rather than a caption, a box and a button. */
.checkout-tier,
.checkout-tier-add {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.checkout-tier-add {
    margin-top: 14px;
}

.checkout-tier .kudio-field-group,
.checkout-tier-add .kudio-field-group {
    flex: 0 1 170px;
}

.button--small { padding: 6px 12px; font-size: 12px; }

/* ---- features ------------------------------------------------------------ */

.checkout-feature {
    padding: 12px 10px;
    border-radius: 10px;
    border: 1px solid transparent;
    transition: background .12s;
}

.checkout-feature:hover { background: #f9fafb; }
.checkout-feature--off { opacity: .55; }

.checkout-feature__head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.checkout-feature__name { font-size: 14px; font-weight: 600; cursor: pointer; }
.checkout-feature__spacer { flex: 1; }
.checkout-feature__price { flex: 0 0 130px; }
.checkout-feature__model { flex: 0 0 190px; }

.checkout-feature__description {
    font-size: 12.5px;
    color: var(--colour-text-muted, #6b7280);
    line-height: 1.5;
    margin: 6px 0 0 26px;
}

.checkout-feature__options {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: 8px 0 0 26px;
}

.checkout-feature__options-label {
    font-size: 11.5px;
    color: var(--colour-text-muted, #9ca3af);
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 600;
}

.checkout-chip {
    font-size: 12px;
    background: var(--accent-bg, #eef3fb);
    border: 1px solid #dce7f6;
    border-radius: 999px;
    padding: 3px 11px;
}


/* ---- checkout: email plans and discount codes ---------------------------- */

.checkout-plan-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 12px;
    margin: 12px 0 0;
}

.checkout-plan-actions {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin: 12px 0 0;
}

.checkout-code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    letter-spacing: .02em;
}

.checkout-code-uses {
    font-size: 12px;
    color: var(--colour-text-muted, #6b7280);
    white-space: nowrap;
}


/* ---- checkout: two columns of sections ----------------------------------- */

/* The form/preview split above puts a narrow second column beside a wide one.
   The Checkouts screens want two columns of equal weight instead, because both
   sides are sections of the same form rather than a form and a picture of it.
   A modifier on the existing grid rather than a second grid, so the gap, the
   alignment and the width it gives up at stay one decision. */
.portal-settings-layout--even {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* The collapse rule for the base class sits earlier in this file, so it loses
   to the modifier above on equal specificity. The modifier has to say where it
   collapses itself, at the same width, or two columns survive onto a phone. */
@media (max-width: 1100px) {
    .portal-settings-layout--even {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Grid children default to min-width: auto, so one long unbroken value - a URL,
   a domain list - widens its column and pushes the page sideways instead of
   wrapping. This class was already in the markup with no rule behind it. */
.portal-settings-form,
.settings-column {
    min-width: 0;
}

/* ---- checkout: the public link ------------------------------------------- */

.checkout-link-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.checkout-link-row .kudio-field-group {
    flex: 1 1 260px;
}

/* Said plainly rather than shown as a broken link, because a link that looks
   real and goes nowhere is worse than an admission. */
.checkout-link-missing {
    margin: 0;
    font-size: var(--font-size-small);
    color: var(--text-muted);
    line-height: 1.55;
}

/* ---- checkout: the generated embed code ---------------------------------- */

.embed-modal {
    width: 100%;
    max-width: 860px;
    max-height: calc(100vh - 48px);
    display: flex;
    flex-direction: column;
    padding: 20px 22px;
    border-radius: var(--radius-large);
    background: var(--surface-panel);
    box-shadow: var(--shadow-dropdown);
}

.embed-modal__title {
    margin: 0 0 6px;
    font-size: var(--font-size-medium);
    font-weight: 700;
    color: var(--text-primary);
}

.embed-modal__note {
    margin: 0 0 14px;
    font-size: var(--font-size-small);
    line-height: 1.6;
    color: var(--text-secondary);
}

/* Read-only and monospaced: this is the artefact, and somebody will want to
   scan it for what it contains before pasting it onto their own website. */
.embed-modal__code {
    flex: 1;
    min-height: 220px;
    width: 100%;
    padding: 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-medium);
    background: var(--surface-subtle);
    font-family: var(--font-monospace);
    font-size: var(--font-size-small);
    line-height: 1.5;
    color: var(--text-secondary);
    white-space: pre;
    overflow: auto;
    resize: none;
}

.embed-modal__actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 16px;
}

.embed-modal__spacer {
    flex: 1;
}


/* =============================================================================
   FILE PREVIEW OVERLAY  (core/file-preview-overlay.js)

   A centred modal that previews one file - PDF or image inline, Office files
   as a download-to-open state with an explicit "preview via Microsoft" step.
   Radii follow the 5px button radius the rest of the shell uses.
   ============================================================================= */

.file-preview-scrim {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(15, 18, 28, 0.55);
    opacity: 0;
    transition: opacity 160ms ease;
}

.file-preview-scrim--open {
    opacity: 1;
}

.file-preview {
    display: flex;
    flex-direction: column;
    width: min(780px, 96vw);
    height: 100%;
    margin-left: auto;
    background: var(--surface, #fff);
    border-radius: var(--radius-large, 5px);
    box-shadow: -18px 0 48px rgba(15, 18, 28, 0.3);
    overflow: hidden;
    transform: translateY(8px);
    transition: transform 240ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.file-preview-scrim--open .file-preview {
    transform: translateY(0);
}

.file-preview__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex: none;
    padding: 12px 14px 12px 16px;
    border-bottom: 1px solid var(--border-subtle, #edeef5);
    background: var(--surface, #fff);
}

.file-preview__identity {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.file-preview__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex: none;
    border-radius: var(--radius-button, 5px);
    background: #eef1f8;
    color: #6b7086;
}

.file-preview__titles {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.file-preview__name {
    font-weight: 600;
    font-size: 14px;
    color: var(--text-primary, #1a1d29);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.file-preview__type {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-secondary, #6b7086);
}

.file-preview__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: none;
}

.file-preview__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: none;
    background: transparent;
    cursor: pointer;
    border-radius: var(--radius-button, 5px);
    color: var(--text-secondary, #6b7086);
}

.file-preview__close:hover {
    background: #eef1f8;
    color: var(--text-primary, #1a1d29);
}

.file-preview__body {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    background: #f4f5f9;
}

.file-preview__frame {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    background: #f4f5f9;
}

.file-preview__image-wrap {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    box-sizing: border-box;
    overflow: auto;
}

.file-preview__image {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: var(--radius-button, 5px);
    box-shadow: 0 8px 24px rgba(15, 18, 28, 0.18);
}

.file-preview__fallback {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 8px;
    padding: 32px;
    box-sizing: border-box;
}

.file-preview__fallback-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 76px;
    height: 76px;
    margin-bottom: 6px;
    border-radius: var(--radius-button, 5px);
    background: #eef1f8;
    color: #6b7086;
}

.file-preview__fallback-title {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary, #1a1d29);
}

.file-preview__fallback-text {
    margin: 0;
    max-width: 440px;
    font-size: 13px;
    color: var(--text-secondary, #6b7086);
}

.file-preview__fallback-actions {
    display: flex;
    gap: 10px;
    margin-top: 12px;
    flex-wrap: wrap;
    justify-content: center;
}

.file-preview__badge--pdf    { background: #fdecec; color: #d64545; }
.file-preview__badge--word   { background: #e9f0fe; color: #2563eb; }
.file-preview__badge--excel  { background: #e7f6ee; color: #15803d; }
.file-preview__badge--slides { background: #fdeee1; color: #d97706; }
.file-preview__badge--image  { background: #f0ecfe; color: #7c3aed; }
.file-preview__badge--text   { background: #eef1f8; color: #4b5468; }

@media (max-width: 640px) {
    .file-preview-scrim { padding: 0; }
    .file-preview {
        width: 100vw;
        height: 100vh;
        border-radius: 0;
    }
    .file-preview__actions .button span { display: none; }
}


/* The file preview is a slide-over from the right, flush to the edge, rather
   than a centred lightbox - the same side the record panels open from, so it
   reads as part of the same furniture. These win over the centred defaults
   declared above. */
.file-preview-scrim {
    align-items: stretch;
    justify-content: flex-end;
    padding: 0;
}

.file-preview {
    transform: translateX(100%);
}

.file-preview-scrim--open .file-preview {
    transform: translateX(0);
}


/* Start below the top header, exactly like the record slide-over, so the
   global controls in the header stay reachable while a preview is open. This
   overrides the `inset: 0` top from the base rule. */
.file-preview-scrim {
    top: var(--height-top-header);
}


/* -------------------------------------------------------------------------
   File preview - the security check states

   Shown before the file itself: a scanning state the first time a file is
   opened, a blocking danger state when it is flagged, and a thin caution bar
   above the file when it could not be fully verified.
   ------------------------------------------------------------------------- */

.file-preview__state {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 8px;
    padding: 32px;
    box-sizing: border-box;
}

.file-preview__state-title {
    margin: 4px 0 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary, #1a1d29);
}

.file-preview__state-text {
    margin: 0;
    max-width: 420px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-secondary, #6b7086);
}

.file-preview__state-note {
    margin: 6px 0 0;
    font-size: 12px;
    color: var(--text-muted, #98a0b3);
    word-break: break-all;
    max-width: 420px;
}

/* A quiet spinner ring in the app accent while the scan runs. */
.file-preview__spinner {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 3px solid #e2e6f0;
    border-top-color: var(--accent, #4f46e5);
    animation: file-preview-spin 0.7s linear infinite;
}

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

.file-preview__state-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 76px;
    height: 76px;
    margin-bottom: 6px;
    border-radius: var(--radius-button, 5px);
}

.file-preview__state-badge--danger {
    background: var(--status-danger-tint, #fdecec);
    color: var(--status-danger, #d64545);
}

.file-preview__state--danger .file-preview__state-title {
    color: var(--status-danger, #d64545);
}

/* The caution bar for files that opened but could not be fully verified. */
.file-preview__notice {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: none;
    padding: 9px 16px;
    font-size: 12.5px;
    line-height: 1.4;
    color: #4a5468;
    background: #eef2f8;
    border-bottom: 1px solid #dbe1ea;
}

.file-preview__notice .icon {
    flex: none;
    color: #6b7ba0;
}

@media (prefers-reduced-motion: reduce) {
    .file-preview__spinner { animation-duration: 2s; }
}

/* Contact panel (person beside an organisation) + Contacts tab */
.contact-form { display: flex; flex-direction: column; gap: 16px; }
.contact-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border-subtle, #e6e8ef); margin-bottom: 4px; }
.contact-tab { appearance: none; background: none; border: none; padding: 8px 12px; font-size: 13px; font-weight: 600; color: var(--text-secondary, #6b7086); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.contact-tab:hover { color: var(--text-primary, #242833); }
.contact-tab.is-active { color: var(--accent, #6366f1); border-bottom-color: var(--accent, #6366f1); }
.contact-primary-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: 13px; color: var(--text-primary); cursor: pointer;
}
.contact-primary-toggle input {
    width: 15px; height: 15px; accent-color: var(--accent, #6366f1);
}
.contact-primary-badge {
    display: inline-flex; align-items: center;
    padding: 2px 8px; font-size: 11px; font-weight: 600;
    color: #166534; background: #dcfce7;
    border: 1px solid #bbf7d0; border-radius: var(--radius-button);
}
.contact-org-links { display: flex; flex-direction: column; gap: 6px; }
.contact-org-cell { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.contact-org-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; font-size: 12px; font-weight: 600; color: var(--text-primary, #1f2430); background: var(--surface-hover, #f1f3f7); border: 1px solid var(--border-subtle, #e3e7ee); border-radius: var(--radius-button); }
.list-row--clickable { cursor: pointer; }
.contact-org-links__row {
    display: flex; align-items: center; gap: 8px;
    background: var(--surface-hover, #f1f3f7);
    border-radius: var(--radius-button); padding: 8px 10px;
    color: var(--text-muted, #6b7280);
}
.contact-org-links__name {
    flex: 1 1 auto; font-size: 13px; font-weight: 600; color: var(--text-primary);
}
