/* ============================================================================
 * ems.css — EMS component layer (Phase 1)
 * ----------------------------------------------------------------------------
 * Loaded by includes/layouts/partials/tw_head.php, so it reaches the admin
 * panel (app.php), the portal (portal.php) and bare pages (bare.php).
 *
 * It does NOT reach the login / forgot-password screens: those render in
 * glass.php with css/theme.css + css/custom.css, and stay exactly as they are.
 *
 * Phase 1 is additive only. Every class here is `ems-` prefixed, and no view
 * uses these names yet, so loading this file changes nothing on screen. Views
 * move onto the classes one at a time in the later phases.
 *
 * Two rules to keep it that way:
 *   1. Never style a bare element (`table`, `button`, `a`) — only `.ems-*`.
 *   2. Keep every rule at single-class specificity, so a Tailwind utility
 *      written next to a component class still wins. The Play CDN injects its
 *      stylesheet after this file, so `class="ems-btn mt-6"` behaves as
 *      expected.
 * ========================================================================== */

/* ---------------------------------------------------------------------------
 * 1. Tokens
 * The values mirror the Tailwind theme in tw_head.php, restated as custom
 * properties so a theme switch is a token flip rather than a class rewrite.
 * ------------------------------------------------------------------------- */
:root {
    /* Brand */
    --ems-brand:            #3e2723;
    --ems-brand-hover:      #33201d;
    --ems-brand-ink:        #5b403c;
    --ems-brand-wash:       #f6efed;
    --ems-on-brand:         #ffffff;

    /* Surfaces */
    --ems-ground:           #f7f9fc;
    --ems-paper:            #ffffff;
    --ems-paper-2:          #faf9f8;
    --ems-paper-3:          #f2f4f7;

    /* Ink */
    --ems-ink:              #191c1e;
    --ems-ink-2:            #504442;
    --ems-ink-3:            #827472;

    /* Rules — warmer and lighter than the current #d3c3c0 outline-variant */
    --ems-rule:             #e6e0de;
    --ems-rule-soft:        #f2eeed;

    /* Status — one definition for the whole app */
    --ems-ok:               #1e7d4f;
    --ems-ok-bg:            #f2fbf6;
    --ems-ok-rule:          #bfe3d0;
    --ems-warn:             #b97309;
    --ems-warn-bg:          #fdf8ef;
    --ems-warn-rule:        #f0dcb8;
    --ems-err:              #ba1a1a;
    --ems-err-bg:           #fdf3f2;
    --ems-err-rule:         #f3c9c6;
    --ems-info:             #1565c0;
    --ems-info-bg:          #f1f6fd;
    --ems-info-rule:        #c6dcf5;
    --ems-idle:             #6b7280;
    --ems-idle-bg:          #f7f8fa;
    --ems-idle-rule:        #e2e5e9;

    /* Shape */
    --ems-r-control:        7px;
    --ems-r-card:           10px;
    --ems-r-pill:           999px;

    /* Rhythm */
    --ems-gutter:           18px;
    --ems-gap:              12px;

    /* Type */
    --ems-fs-title:         21px;
    --ems-fs-section:       15px;
    --ems-fs-card:          14px;
    --ems-fs-body:          13px;
    --ems-fs-meta:          11.5px;
    --ems-fs-label:         10px;

    --ems-focus:            0 0 0 3px rgba(62, 39, 35, .22);
}

/* Dark palette. Nothing sets these hooks yet — `<html class="light">` is still
 * hard-coded in the layouts — so this block is inert until phase 7 adds the
 * toggle. Both hooks are here because Tailwind is configured `darkMode:"class"`
 * while `[data-theme]` is the friendlier attribute to flip server-side. */
html.dark,
html[data-theme="dark"] {
    --ems-brand:            #e3beb8;
    --ems-brand-hover:      #f0d3ce;
    --ems-brand-ink:        #e3beb8;
    --ems-brand-wash:       #2a1f1d;
    --ems-on-brand:         #241a18;

    --ems-ground:           #111315;
    --ems-paper:            #191c1f;
    --ems-paper-2:          #1f2226;
    --ems-paper-3:          #22262a;

    --ems-ink:              #e9ebee;
    --ems-ink-2:            #b0b6bd;
    --ems-ink-3:            #848b93;

    --ems-rule:             #2c3035;
    --ems-rule-soft:        #23272b;

    --ems-ok:               #4cc38a;  --ems-ok-bg:   #14251d;  --ems-ok-rule:   #23492f;
    --ems-warn:             #e0a458;  --ems-warn-bg: #241d12;  --ems-warn-rule: #4a3820;
    --ems-err:              #ff8a80;  --ems-err-bg:  #2a1615;  --ems-err-rule:  #552a27;
    --ems-info:             #7ab3f0;  --ems-info-bg: #131d29;  --ems-info-rule: #23394f;
    --ems-idle:             #9aa1a9;  --ems-idle-bg: #1d2023;  --ems-idle-rule: #2c3035;

    --ems-focus:            0 0 0 3px rgba(227, 190, 184, .25);
}

/* ---------------------------------------------------------------------------
 * 2. Buttons
 * `.ems-btn` is the outlined default — the shape 15 pages currently spell out
 * as `px-3 py-2 rounded-lg border border-outline-variant text-label-md …`.
 * ------------------------------------------------------------------------- */
.ems-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 6px 11px;
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-control);
    background: var(--ems-paper);
    color: var(--ems-ink);
    font: inherit;
    font-size: var(--ems-fs-meta);
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
    transition: background-color .15s, border-color .15s, color .15s;
}
.ems-btn:hover        { background: var(--ems-paper-2); border-color: var(--ems-ink-3); }
.ems-btn:focus-visible{ outline: none; box-shadow: var(--ems-focus); }
.ems-btn:disabled,
.ems-btn[aria-disabled="true"] { opacity: .5; pointer-events: none; }

.ems-btn--primary  { background: var(--ems-brand); border-color: var(--ems-brand); color: var(--ems-on-brand); }
.ems-btn--primary:hover { background: var(--ems-brand-hover); border-color: var(--ems-brand-hover); }

.ems-btn--ghost    { background: transparent; border-color: transparent; color: var(--ems-ink-2); }
.ems-btn--ghost:hover { background: var(--ems-paper-3); border-color: transparent; color: var(--ems-ink); }

.ems-btn--danger   { background: transparent; border-color: var(--ems-err-rule); color: var(--ems-err); }
.ems-btn--danger:hover { background: var(--ems-err-bg); border-color: var(--ems-err); }

.ems-btn--sm       { padding: 4px 8px; font-size: var(--ems-fs-label); }
.ems-btn--lg       { padding: 9px 16px; font-size: var(--ems-fs-body); }
.ems-btn--icon     { padding: 6px; width: 30px; }
.ems-btn--block    { display: flex; width: 100%; }

/* Material Symbols sit slightly high next to 12.5px text. */
.ems-btn .material-symbols-outlined { font-size: 16px; line-height: 1; }

/* ---------------------------------------------------------------------------
 * 3. Card
 * Replaces the `bg-white rounded-lg border border-outline-variant shadow-sm`
 * chain typed out in 89 places. One hairline, no drop shadow.
 * ------------------------------------------------------------------------- */
.ems-card {
    background: var(--ems-paper);
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-card);
    overflow: hidden;
}
.ems-card__head {
    display: flex;
    align-items: center;
    /* Not space-between: a head holding just an icon and a title would push
     * them to opposite ends of the card. The heading claims the slack instead,
     * so an icon stays beside its title and any action after it goes right. */
    justify-content: flex-start;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 14px;
    border-bottom: 1px solid var(--ems-rule-soft);
}
/* Push the trailing element right — but only when it is an action. A head is
 * written either as [icon][title] or as [title][action], and the title is
 * often wrapped in a div with its icon, so keying on the heading tag alone
 * misses half the cases. The last child gets the slack unless it is the
 * heading itself. */
.ems-card__head > :last-child:not(:first-child):not(h1):not(h2):not(h3):not(h4):not(.ems-card__title) {
    margin-left: auto;
}
.ems-card__title {
    font-size: var(--ems-fs-card);
    font-weight: 600;
    color: var(--ems-ink);
    margin: 0;
}
.ems-card__sub {
    font-size: var(--ems-fs-meta);
    color: var(--ems-ink-3);
    margin: 2px 0 0;
}
.ems-card__body { padding: 14px; }
.ems-card__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 14px;
    border-top: 1px solid var(--ems-rule-soft);
    font-size: var(--ems-fs-meta);
    color: var(--ems-ink-3);
}
/* A card whose body is a table wants no padding around it. */
.ems-card__body--flush { padding: 0; }

/* Same padding and rule as __head, but it stays a block. Many section heads
 * stack a title over a line of help text; __head is a flex row and would sit
 * them side by side. */
.ems-card__band {
    padding: 10px 14px;
    border-bottom: 1px solid var(--ems-rule-soft);
}

/* ---------------------------------------------------------------------------
 * 4. Toolbar — search, filters, count. Sits above a table inside a card.
 * ------------------------------------------------------------------------- */
.ems-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
    padding: 9px 12px;
    border-bottom: 1px solid var(--ems-rule-soft);
}
.ems-toolbar__group { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ems-toolbar__grow  { flex: 1 1 220px; min-width: 180px; }
.ems-toolbar__count { font-size: var(--ems-fs-meta); color: var(--ems-ink-3); }
.ems-toolbar__count b { color: var(--ems-ink); font-weight: 600; }

/* ---------------------------------------------------------------------------
 * 5. Form controls
 * ------------------------------------------------------------------------- */
.ems-input,
.ems-select,
.ems-textarea {
    width: 100%;
    padding: 6px 10px;
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-control);
    background: var(--ems-paper-2);
    color: var(--ems-ink);
    font: inherit;
    font-size: var(--ems-fs-body);
    line-height: 1.4;
}
.ems-input::placeholder,
.ems-textarea::placeholder { color: var(--ems-ink-3); }
.ems-input:focus,
.ems-select:focus,
.ems-textarea:focus {
    outline: none;
    border-color: var(--ems-brand);
    box-shadow: var(--ems-focus);
    background: var(--ems-paper);
}
.ems-select  { padding-right: 34px; }
.ems-textarea{ min-height: 78px; resize: vertical; }

/* Input with a leading icon (search boxes, icon-marked fields).
   Wrap in `.ems-search`, put the icon in `.ems-search__icon`. The two-class
   selector on the input keeps the reserved room even when a Tailwind utility
   sits on the same element, so the text never runs under the icon. */
.ems-search { position: relative; }
/* The class is repeated so this outranks both the forms plugin and the
   `.ems-input.ems-input` reset in section 20 below, which would otherwise
   put the padding back to 10px and slide the text under the icon. */
.ems-search .ems-input.ems-input,
.ems-search .ems-select.ems-select { padding-left: 38px; }
.ems-search__icon {
    position: absolute;
    left: 11px;
    top: 50%;
    transform: translateY(-50%);
    display: block;
    line-height: 1;
    color: var(--ems-ink-3);
    font-size: 20px;
    pointer-events: none;
}

.ems-field { display: block; margin-bottom: var(--ems-gap); }
.ems-field__label {
    display: block;
    margin-bottom: 5px;
    font-size: var(--ems-fs-meta);
    font-weight: 600;
    color: var(--ems-ink-2);
}
.ems-field__label--req::after { content: " *"; color: var(--ems-err); }
.ems-field__hint {
    display: block;
    margin-top: 5px;
    font-size: var(--ems-fs-meta);
    color: var(--ems-ink-3);
}
.ems-field__error {
    display: block;
    margin-top: 5px;
    font-size: var(--ems-fs-meta);
    color: var(--ems-err);
}
.ems-field--invalid .ems-input,
.ems-field--invalid .ems-select,
.ems-field--invalid .ems-textarea { border-color: var(--ems-err); }

/* Segmented control — the two-to-five-way filter a <select> is overkill for. */
.ems-seg {
    display: inline-flex;
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-control);
    overflow: hidden;
}
.ems-seg > * {
    padding: 5px 11px;
    border: 0;
    border-right: 1px solid var(--ems-rule);
    background: var(--ems-paper);
    color: var(--ems-ink-2);
    font: inherit;
    font-size: var(--ems-fs-meta);
    font-weight: 500;
    line-height: 1.2;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
}
.ems-seg > *:last-child { border-right: 0; }
.ems-seg > *:hover { background: var(--ems-paper-3); }
.ems-seg > .is-on {
    background: var(--ems-brand);
    border-right-color: var(--ems-brand);
    color: var(--ems-on-brand);
    font-weight: 600;
}

/* ---------------------------------------------------------------------------
 * 6. Table
 * `.ems-tbl` is the default; `.ems-tbl--dense` is the roster density. Both are
 * lighter than today's `px-6 py-4` rows under bold uppercase headers.
 * ------------------------------------------------------------------------- */
.ems-tblwrap { overflow-x: auto; }

.ems-tbl {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--ems-fs-body);
    color: var(--ems-ink);
}
.ems-tbl th {
    position: sticky;
    top: 0;
    z-index: 1;
    text-align: left;
    padding: 7px 12px;
    background: var(--ems-paper-2);
    border-bottom: 1px solid var(--ems-rule);
    font-size: var(--ems-fs-meta);
    font-weight: 600;
    color: var(--ems-ink-3);
    white-space: nowrap;
}
.ems-tbl td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--ems-rule-soft);
    vertical-align: middle;
}
.ems-tbl tbody tr:last-child td { border-bottom: 0; }
.ems-tbl tbody tr:hover td { background: var(--ems-brand-wash); }

.ems-tbl--dense td { padding: 5px 10px; }
.ems-tbl--dense th { padding: 5px 10px; }

/* Secondary line under a primary cell value — degree, faculty, email. */
.ems-tbl__sub {
    display: block;
    margin-top: 2px;
    font-size: var(--ems-fs-meta);
    color: var(--ems-ink-3);
}
/* Right-align and line up digits. */
.ems-num { text-align: right; font-variant-numeric: tabular-nums; }
.ems-nums{ font-variant-numeric: tabular-nums; }

/* Keep a row's actions reachable while the table scrolls sideways. */
.ems-tbl__actions {
    position: sticky;
    right: 0;
    background: var(--ems-paper);
    text-align: right;
    white-space: nowrap;
}
.ems-tbl tbody tr:hover .ems-tbl__actions { background: var(--ems-brand-wash); }

/* ---------------------------------------------------------------------------
 * 7. Status pill
 * One vocabulary for Active / Ongoing / Finished / Inactive / Pending, in place
 * of the ~250 raw `bg-green-100 text-green-700` pairs across the views.
 * ------------------------------------------------------------------------- */
.ems-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px 2px 6px;
    border: 1px solid var(--ems-idle-rule);
    border-radius: var(--ems-r-pill);
    background: var(--ems-idle-bg);
    color: var(--ems-idle);
    font-size: var(--ems-fs-label);
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
}
.ems-pill::before {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}
.ems-pill--ok   { color: var(--ems-ok);   background: var(--ems-ok-bg);   border-color: var(--ems-ok-rule); }
.ems-pill--warn { color: var(--ems-warn); background: var(--ems-warn-bg); border-color: var(--ems-warn-rule); }
.ems-pill--err  { color: var(--ems-err);  background: var(--ems-err-bg);  border-color: var(--ems-err-rule); }
.ems-pill--info { color: var(--ems-info); background: var(--ems-info-bg); border-color: var(--ems-info-rule); }
.ems-pill--idle { color: var(--ems-idle); background: var(--ems-idle-bg); border-color: var(--ems-idle-rule); }
/* Count badges and plain tags — same shape, no status dot. */
.ems-pill--plain::before { display: none; }
.ems-pill--plain { padding-left: 10px; }

/* ---------------------------------------------------------------------------
 * 8. Inline notice — the banner that today is re-typed with amber-50 / red-50
 * ------------------------------------------------------------------------- */
.ems-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 9px 12px;
    border: 1px solid var(--ems-info-rule);
    border-radius: var(--ems-r-control);
    background: var(--ems-info-bg);
    color: var(--ems-ink-2);
    font-size: var(--ems-fs-body);
}
.ems-note b { color: var(--ems-ink); font-weight: 600; }
.ems-note .material-symbols-outlined { font-size: 18px; color: var(--ems-info); flex: none; }
.ems-note--ok   { border-color: var(--ems-ok-rule);   background: var(--ems-ok-bg); }
.ems-note--ok   .material-symbols-outlined { color: var(--ems-ok); }
.ems-note--warn { border-color: var(--ems-warn-rule); background: var(--ems-warn-bg); }
.ems-note--warn .material-symbols-outlined { color: var(--ems-warn); }
.ems-note--err  { border-color: var(--ems-err-rule);  background: var(--ems-err-bg); }
.ems-note--err  .material-symbols-outlined { color: var(--ems-err); }

/* ---------------------------------------------------------------------------
 * 9. Empty state — icon, one line, one action. Replaces the bare
 * "No records found." cell.
 * ------------------------------------------------------------------------- */
.ems-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 34px 18px;
    text-align: center;
    color: var(--ems-ink-3);
}
.ems-empty .material-symbols-outlined {
    font-size: 32px;
    color: var(--ems-rule);
}
.ems-empty__title {
    font-size: var(--ems-fs-card);
    font-weight: 600;
    color: var(--ems-ink);
}
.ems-empty__text { font-size: var(--ems-fs-body); max-width: 46ch; }

/* ---------------------------------------------------------------------------
 * 10. KPI tile
 * ------------------------------------------------------------------------- */
.ems-kpi {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    background: var(--ems-paper);
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-card);
}
.ems-kpi__label {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: var(--ems-fs-label);
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ems-ink-3);
}
.ems-kpi__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ems-idle); flex: none; }
.ems-kpi__dot--ok   { background: var(--ems-ok); }
.ems-kpi__dot--warn { background: var(--ems-warn); }
.ems-kpi__dot--err  { background: var(--ems-err); }
.ems-kpi__dot--info { background: var(--ems-info); }
.ems-kpi__value {
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -.02em;
    line-height: 1;
    color: var(--ems-ink);
    font-variant-numeric: tabular-nums;
}
.ems-kpi__delta { font-size: var(--ems-fs-meta); font-weight: 600; color: var(--ems-ink-3); }
.ems-kpi__delta--up   { color: var(--ems-ok); }
.ems-kpi__delta--down { color: var(--ems-err); }

/* ---------------------------------------------------------------------------
 * 11. Meter + step list — the event setup sequence
 * ------------------------------------------------------------------------- */
.ems-meter {
    height: 6px;
    border-radius: var(--ems-r-pill);
    background: var(--ems-rule-soft);
    overflow: hidden;
}
.ems-meter__fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: var(--ems-r-pill);
    background: var(--ems-brand);
    transition: width .3s;
}

.ems-step {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--ems-rule-soft);
    text-decoration: none;
    color: inherit;
}
.ems-step:last-child { border-bottom: 0; }
.ems-step:hover { background: var(--ems-paper-2); }
.ems-step__n {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: 1.5px solid var(--ems-rule);
    border-radius: 50%;
    font-size: var(--ems-fs-label);
    font-weight: 700;
    color: var(--ems-ink-3);
    flex: none;
}
.ems-step--done .ems-step__n { background: var(--ems-ok); border-color: var(--ems-ok); color: #fff; }
.ems-step--cur  .ems-step__n { border-color: var(--ems-brand); color: var(--ems-brand); }
.ems-step__body { flex: 1; min-width: 0; }
.ems-step__title{ display: block; font-size: var(--ems-fs-body); font-weight: 600; }
.ems-step__meta { display: block; font-size: var(--ems-fs-meta); color: var(--ems-ink-3); }
.ems-step__go   { font-size: var(--ems-fs-meta); font-weight: 600; color: var(--ems-brand); white-space: nowrap; }

/* ---------------------------------------------------------------------------
 * 12. Pagination
 * ------------------------------------------------------------------------- */
.ems-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    padding: 9px 12px;
    border-top: 1px solid var(--ems-rule-soft);
    font-size: var(--ems-fs-meta);
    color: var(--ems-ink-3);
}
.ems-pager__range { font-variant-numeric: tabular-nums; }
.ems-pager__links { display: flex; align-items: center; gap: 6px; }
.ems-pager__links a,
.ems-pager__links span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 26px;
    padding: 0 8px;
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-control);
    background: var(--ems-paper);
    color: var(--ems-ink-2);
    font-weight: 600;
    text-decoration: none;
    font-variant-numeric: tabular-nums;
}
.ems-pager__links a:hover { background: var(--ems-paper-3); }
.ems-pager__links .is-on {
    background: var(--ems-brand);
    border-color: var(--ems-brand);
    color: var(--ems-on-brand);
}
.ems-pager__links .is-off { opacity: .45; pointer-events: none; }

/* ---------------------------------------------------------------------------
 * 13. Small helpers
 * ------------------------------------------------------------------------- */
.ems-grid       { display: grid; gap: var(--ems-gap); }
.ems-grid--2    { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ems-grid--3    { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ems-grid--4    { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ems-grid--auto { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
@media (max-width: 900px) {
    .ems-grid--3, .ems-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
    .ems-grid--2, .ems-grid--3, .ems-grid--4 { grid-template-columns: minmax(0, 1fr); }
}

.ems-label {
    font-size: var(--ems-fs-label);
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ems-ink-3);
}
.ems-muted  { color: var(--ems-ink-3); }
.ems-divider{ height: 1px; background: var(--ems-rule-soft); border: 0; margin: var(--ems-gap) 0; }

@media (prefers-reduced-motion: reduce) {
    .ems-btn, .ems-meter__fill { transition: none; }
}

/* Tables and toolbars are chrome — keep them off the printed roster. */
@media print {
    .ems-toolbar, .ems-pager, .ems-btn { display: none !important; }
    .ems-card { border: 0; }
    .ems-tbl th { position: static; }
}

/* ---------------------------------------------------------------------------
 * 14. Busy states
 * Anything that talks to a mail or SMS gateway takes seconds, sometimes minutes
 * for a bulk run. Before this the page sat still and the only signal was an
 * alert() long after the fact, so people clicked Send twice.
 *
 * Keyframe is `ems-rotate`, not `ems-spin` — views/web/admin/events/setup.php
 * already defines its own `ems-spin` animation locally.
 * ------------------------------------------------------------------------- */
@keyframes ems-rotate { to { transform: rotate(360deg); } }
@keyframes ems-pulse  { 50% { opacity: .35; } }

.ems-spinner {
    display: inline-block;
    flex: none;
    width: 16px;
    height: 16px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: ems-rotate .7s linear infinite;
}
.ems-spinner--lg { width: 30px; height: 30px; border-width: 3px; }

/* A button mid-request: label stays readable, nothing is clickable twice. */
.ems-btn.is-busy {
    pointer-events: none;
    opacity: .8;
}
.ems-btn.is-busy .material-symbols-outlined { display: none; }

/* Full-screen block for an action the whole page is waiting on. */
.ems-busy {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(25, 28, 30, .45);
}
.ems-busy[hidden] { display: none; }
.ems-busy__card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    max-width: 340px;
    padding: 28px 32px;
    border-radius: var(--ems-r-card);
    background: var(--ems-paper);
    color: var(--ems-ink);
    text-align: center;
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .5);
}
.ems-busy__card .ems-spinner { color: var(--ems-brand); }
.ems-busy__msg { font-size: var(--ems-fs-card); font-weight: 600; }
.ems-busy__sub { font-size: var(--ems-fs-meta); color: var(--ems-ink-3); }

/* Thin indeterminate bar for background fetches that do not block the page. */
.ems-topload {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    z-index: 210;
    overflow: hidden;
    background: transparent;
}
.ems-topload[hidden] { display: none; }
.ems-topload::after {
    content: "";
    position: absolute;
    inset: 0;
    width: 40%;
    background: var(--ems-brand);
    animation: ems-slide 1.1s ease-in-out infinite;
}
@keyframes ems-slide {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(350%); }
}

/* Skeleton placeholder for a pane that is still loading. */
.ems-skeleton {
    border-radius: var(--ems-r-control);
    background: var(--ems-rule-soft);
    animation: ems-pulse 1.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
    .ems-spinner { animation: ems-pulse 1.2s ease-in-out infinite; border-right-color: currentColor; }
    .ems-topload::after { animation: ems-pulse 1.2s ease-in-out infinite; width: 100%; }
    .ems-skeleton { animation: none; }
}

/* ---------------------------------------------------------------------------
 * Navbar email-progress menu — rows for the header mail icon's dropdown. Each
 * bulk email send now runs in the background on the mail service; this lists the
 * running + recently finished ones with a progress bar. Driven by EMS.campaign /
 * the feed poller in ems_js. The dropdown shell itself is Tailwind (app.php);
 * only the row internals live here.
 * ------------------------------------------------------------------------- */
/* Sending state: fade in/out rather than spin — a rotating navbar icon reads
   as "loading/broken"; a gentle opacity pulse reads as "quietly working". */
.ems-cfeed__navspin { animation: ems-pulse 1.4s ease-in-out infinite; }

.ems-cfeed__row {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 10px 16px;
    border-bottom: 1px solid var(--ems-rule);
    color: var(--ems-ink);
    text-decoration: none;
}
.ems-cfeed__row:last-child { border-bottom: none; }
a.ems-cfeed__row:hover { background: var(--ems-rule-soft, rgba(0,0,0,.04)); }

.ems-cfeed__icon { font-size: 20px; line-height: 1.2; color: var(--ems-brand); flex: none; }
.ems-cfeed__icon--spin { animation: ems-pulse 1.4s ease-in-out infinite; }

.ems-cfeed__body { min-width: 0; flex: 1; }
.ems-cfeed__title {
    display: block;
    font-size: var(--ems-fs-meta);
    font-weight: 600;
    color: var(--ems-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ems-cfeed__bar {
    display: block;
    height: 6px;
    margin: 6px 0 4px;
    border-radius: 3px;
    background: var(--ems-rule);
    overflow: hidden;
}
.ems-cfeed__fill {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: var(--ems-brand);
    transition: width .5s ease;
}
.ems-cfeed__fill--done { background: var(--ems-ok); }
.ems-cfeed__fill--fail { background: var(--ems-err); }
.ems-cfeed__meta { display: block; font-size: 12px; color: var(--ems-ink-3); }

@media (prefers-reduced-motion: reduce) {
    .ems-cfeed__navspin, .ems-cfeed__icon--spin { animation: ems-pulse 1.4s ease-in-out infinite; }
    .ems-cfeed__fill { transition: none; }
}

/* ---------------------------------------------------------------------------
 * 15. Status dot
 * A pill carries its own dot, but timelines and audit rows use a bare dot with
 * no label beside it. Same five meanings, so the same five names.
 * ------------------------------------------------------------------------- */
.ems-dot {
    display: inline-block;
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ems-idle);
}
.ems-dot--ok   { background: var(--ems-ok); }
.ems-dot--warn { background: var(--ems-warn); }
.ems-dot--err  { background: var(--ems-err); }
.ems-dot--info { background: var(--ems-info); }
.ems-dot--idle { background: var(--ems-idle); }

/* ---------------------------------------------------------------------------
 * 16. Small screens
 * The components are already fluid; this is the density step. A phone gets the
 * tighter padding and the toolbar stacks instead of squeezing, so a filter row
 * never pushes the page sideways.
 * ------------------------------------------------------------------------- */
.ems-tblwrap { -webkit-overflow-scrolling: touch; }

@media (max-width: 640px) {
    .ems-card__body--flush { overflow-x: auto; }
    .ems-card__body        { padding: 11px; }
    .ems-card__head        { padding: 9px 11px; }

    .ems-toolbar           { align-items: stretch; padding: 8px 10px; }
    .ems-toolbar__grow     { flex: 1 1 100%; }

    .ems-tbl th,
    .ems-tbl td            { padding: 6px 9px; }
    .ems-tbl--dense th,
    .ems-tbl--dense td     { padding: 4px 8px; }

    .ems-kpi               { padding: 10px 12px; }
    .ems-kpi__value        { font-size: 21px; }

    .ems-pager             { justify-content: center; }
    .ems-step              { padding: 8px 10px; }
    .ems-empty             { padding: 26px 14px; }

    /* Touch targets stay reachable even as everything else tightens. */
    .ems-btn               { padding: 7px 11px; min-height: 32px; }
    .ems-btn--icon         { width: 32px; min-height: 32px; }
}

/* Affirmative actions — approve, verify, mark done. Separate from --primary,
 * which is the brand and says "the main thing on this page", not "this is good".
 * The quiet form is for an icon in a table row, where a filled green block in
 * every line would shout. */
.ems-btn--ok       { background: var(--ems-ok); border-color: var(--ems-ok); color: #fff; }
.ems-btn--ok:hover { background: color-mix(in srgb, var(--ems-ok) 85%, #000); border-color: color-mix(in srgb, var(--ems-ok) 85%, #000); }
.ems-btn--ok-quiet       { background: transparent; border-color: transparent; color: var(--ems-ok); }
.ems-btn--ok-quiet:hover { background: var(--ems-ok-bg); border-color: transparent; }
.ems-pager--center { justify-content: center; }

/* ---------------------------------------------------------------------------
 * 17. Controls inside a toolbar
 * .ems-input / .ems-select are full-width because most of their uses are in a
 * stacked form. In a filter toolbar that made every control claim its own row,
 * which is how the participants filter ended up stacked under its search box.
 * ------------------------------------------------------------------------- */
.ems-toolbar .ems-select,
.ems-toolbar .ems-input--auto { width: auto; min-width: 140px; }
.ems-toolbar form             { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex: 1; }
.ems-toolbar .ems-search      { flex: 1 1 200px; min-width: 160px; }

@media (max-width: 640px) {
    .ems-toolbar .ems-select,
    .ems-toolbar .ems-input--auto { width: 100%; }
}

/* A KPI that carries its own breakdown: the headline number, a unit, and the
 * parts that make it up. "Pickup done" only means something against "pickup
 * required", so it belongs inside that card rather than beside it as an equal
 * tile competing for the same attention. */
.ems-kpi__unit {
    font-size: var(--ems-fs-meta);
    font-weight: 500;
    color: var(--ems-ink-3);
    letter-spacing: 0;
    margin-left: 4px;
}
.ems-kpi__break {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    margin: 2px 0 0;
    padding-top: 8px;
    border-top: 1px solid var(--ems-rule-soft);
}
.ems-kpi__break span {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    font-size: var(--ems-fs-meta);
    color: var(--ems-ink-3);
}
.ems-kpi__break b {
    color: var(--ems-ink);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.ems-kpi__break b.is-ok   { color: var(--ems-ok); }
.ems-kpi__break b.is-warn { color: var(--ems-warn); }
.ems-kpi__break b.is-err  { color: var(--ems-err); }

/* ---------------------------------------------------------------------------
 * 18. Expanded table row
 * A row that opens to reveal detail. The detail used to be built from nested
 * cards, which put three borders between the reader and the content — a card,
 * inside a table, inside a card. These are plain sections instead: a small
 * label, a hairline, and the content.
 * ------------------------------------------------------------------------- */
.ems-subrow {
    padding: 0 !important;
    background: var(--ems-paper-2);
    box-shadow: inset 0 1px 0 var(--ems-rule), inset 0 -1px 0 var(--ems-rule);
}
.ems-subsec { display: block; padding: 14px 16px; }
.ems-subsec + .ems-subsec { border-top: 1px solid var(--ems-rule-soft); }
.ems-subsec__label {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0 0 10px;
    font-size: var(--ems-fs-label);
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ems-ink-3);
}
/* The count that follows a section label. */
.ems-subsec__label span {
    padding: 1px 7px;
    border-radius: var(--ems-r-pill);
    background: var(--ems-rule-soft);
    color: var(--ems-ink-2);
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
}

/* One row of controls that belong together. */
.ems-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}
.ems-line:last-child { margin-bottom: 0; }
/* Same reason: every control in the row must be allowed to shrink. */
.ems-line > * { min-width: 0; }
.ems-line .ems-select,
.ems-line .ems-input--auto { width: auto; min-width: 150px; }
@media (max-width: 640px) {
    /* Stack rather than squeeze: a select and its button side by side on a
     * 390px screen leaves neither usable. */
    .ems-line { flex-direction: column; align-items: stretch; }
    .ems-line > * { width: 100%; }
    .ems-line .ems-btn { justify-content: center; }
}

/* A table used as a plain list inside such a section: no header, no card. */
.ems-tbl--plain { background: transparent; }
.ems-tbl--plain td { border-bottom: 1px solid var(--ems-rule-soft); }
.ems-tbl--plain tr:last-child td { border-bottom: 0; }
.ems-tbl--plain tbody tr:hover td { background: transparent; }

/* Scrollable pick list — the named-audience chooser. */
.ems-picklist {
    margin-top: 6px;
    max-height: 176px;
    overflow-y: auto;
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-control);
    background: var(--ems-paper);
}
.ems-picklist > * { border-bottom: 1px solid var(--ems-rule-soft); }
.ems-picklist > *:last-child { border-bottom: 0; }

/* ---------------------------------------------------------------------------
 * 19. Photo albums
 * A gallery is read by looking, so the card leads with the pictures: a cover
 * collage identifies the album far faster than its name does. The card itself
 * only *shows* — name, count, audience. Everything you can change lives behind
 * "Manage", so five controls are not competing for attention before you have
 * worked out which album you are looking at.
 * ------------------------------------------------------------------------- */
.ems-albums {
    display: grid;
    gap: var(--ems-gap);
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    align-items: start;
}
.ems-album { display: flex; flex-direction: column; overflow: hidden; }

.ems-album__cover {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    /* Rows must be definite. With auto rows, `height: 100%` on the images
     * resolves to `auto`, they render at natural size, and the cover blows the
     * whole card out of the grid — which is exactly how this went wrong. */
    grid-template-rows: 1fr 1fr;
    gap: 2px;
    height: 132px;
    overflow: hidden;
    background: var(--ems-rule-soft);
}
.ems-album__cover img {
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    object-fit: cover;
    display: block;
}
/* Fill the frame whatever the count: one photo takes all of it, two split it
 * down the middle, three give the first the full-height left half. */
.ems-album__cover img:only-child { grid-column: span 2; grid-row: span 2; }
.ems-album__cover img:first-child:nth-last-child(2),
.ems-album__cover img:first-child:nth-last-child(2) ~ img { grid-row: span 2; }
.ems-album__cover img:first-child:nth-last-child(3) { grid-row: span 2; }
.ems-album__cover.is-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    color: var(--ems-ink-3);
    font-size: var(--ems-fs-meta);
    background: var(--ems-paper-2);
}
.ems-album__cover.is-empty .material-symbols-outlined { font-size: 26px; color: var(--ems-rule); }
.ems-album__more {
    position: absolute;
    right: 6px;
    bottom: 6px;
    padding: 1px 7px;
    border-radius: var(--ems-r-pill);
    background: rgba(0, 0, 0, .62);
    color: #fff;
    font-size: var(--ems-fs-label);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.ems-album__body { padding: 11px 13px 0; }
.ems-album__name {
    margin: 0;
    font-size: var(--ems-fs-card);
    font-weight: 600;
    color: var(--ems-ink);
    overflow-wrap: anywhere;
}
.ems-album__meta {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 6px;
    font-size: var(--ems-fs-meta);
}
.ems-album__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 11px 13px;
}
/* Manage opens in a dialog rather than expanding inside the card: the card
 * lives in a 260px grid column, and growing a full management UI inside one
 * made that card tower over its neighbours and shove the grid around. A fixed
 * overlay cannot move the grid, and it gets a readable width to work in. */
.ems-dialog {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(25, 28, 30, .5);
}
.ems-dialog[hidden] { display: none; }
.ems-dialog__panel {
    display: flex;
    flex-direction: column;
    width: 100%;
    /* A flex item defaults to min-width:auto, so wide content — a long select,
     * a row of thumbnails — pushes the panel past the viewport instead of
     * shrinking. On a phone that cut the Upload button clean off the screen. */
    min-width: 0;
    max-width: 620px;
    max-height: 86vh;
    border-radius: var(--ems-r-card);
    background: var(--ems-paper);
    box-shadow: 0 24px 60px -18px rgba(0, 0, 0, .5);
    overflow: hidden;
}
.ems-dialog__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--ems-rule);
}
.ems-dialog__title {
    margin: 0;
    font-size: var(--ems-fs-card);
    font-weight: 600;
    color: var(--ems-ink);
    overflow-wrap: anywhere;
}
.ems-dialog__body { overflow-y: auto; overflow-x: hidden; min-width: 0; background: var(--ems-paper-2); }
.ems-dialog__body .ems-subsec:first-child { border-top: 0; }

/* Manage album: settings down the left, the photos on the right. Each column
 * scrolls on its own, so a long photo grid never pushes the forms out of reach.
 * Under 820px they stack and the body scrolls as one. */
.ems-dialog__panel--wide { max-width: 1040px; }
.ems-dialog__split {
    display: grid;
    grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
    min-height: 0;
    overflow: hidden;
}
.ems-dialog__col { min-width: 0; overflow-y: auto; overflow-x: hidden; }
.ems-dialog__col .ems-subsec:first-child { border-top: 0; }
.ems-dialog__col--settings { background: var(--ems-paper-2); }
.ems-dialog__col--photos { border-left: 1px solid var(--ems-rule); background: var(--ems-paper); }
.ems-dialog__col--photos .ems-photos { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 8px; }
.ems-dialog__col--photos .ems-photo img { height: 104px; }
.ems-photos-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 180px;
    border: 1px dashed var(--ems-rule);
    border-radius: 8px;
    color: var(--ems-ink-3, var(--ems-ink-2));
    font-size: var(--ems-fs-meta);
    text-align: center;
    padding: 16px;
}
.ems-photos-empty .material-symbols-outlined { font-size: 28px; color: var(--ems-rule); }
@media (max-width: 820px) {
    .ems-dialog__split { grid-template-columns: 1fr; overflow-y: auto; }
    .ems-dialog__col { overflow: visible; }
    .ems-dialog__col--photos { border-left: 0; border-top: 1px solid var(--ems-rule); }
}

/* Thumbnails inside Manage, each with its own delete and bound name. */
.ems-photos {
    display: grid;
    gap: 6px;
    grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
}
.ems-photo { position: relative; margin: 0; }
.ems-photo img {
    width: 100%;
    height: 64px;
    object-fit: cover;
    border-radius: 6px;
    display: block;
    background: var(--ems-paper);
}
.ems-photo form { position: absolute; top: 3px; right: 3px; }
.ems-photo form button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 17px;
    height: 17px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(0, 0, 0, .45);
    color: #fff;
    cursor: pointer;
    opacity: .8;
    transition: background-color .15s, opacity .15s;
}
.ems-photo form button .material-symbols-outlined { font-size: 13px; }
.ems-photo:hover form button { opacity: 1; }
.ems-photo form button:hover { background: var(--ems-err); opacity: 1; }

/* A file input styled as one control rather than a native widget sitting inside
 * a bordered box. */
.ems-input[type="file"] { padding: 4px 6px; cursor: pointer; }
.ems-input[type="file"]::file-selector-button {
    margin-right: 10px;
    padding: 4px 10px;
    border: 1px solid var(--ems-rule);
    border-radius: 6px;
    background: var(--ems-paper-3);
    color: var(--ems-ink-2);
    font: inherit;
    font-size: var(--ems-fs-meta);
    font-weight: 600;
    cursor: pointer;
}
.ems-input[type="file"]::file-selector-button:hover { background: var(--ems-rule-soft); }
.ems-photo figcaption {
    margin-top: 3px;
    font-size: 10px;
    color: var(--ems-ink-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A checkbox with its label. Deliberately not .ems-line: that stacks its
 * children on a phone, which would drop the label underneath the box it
 * belongs to. */
.ems-check {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: var(--ems-fs-meta);
    color: var(--ems-ink-2);
    cursor: pointer;
}
.ems-check input[type="checkbox"] { flex: none; accent-color: var(--ems-brand); }

/* A KPI that is also a link — the scan purposes filter the report below them. */
a.ems-kpi { color: inherit; transition: border-color .15s, background-color .15s; }
a.ems-kpi:hover { border-color: var(--ems-brand); }
a.ems-kpi.is-on { border-color: var(--ems-brand); background: var(--ems-brand-wash); }

/* ---------------------------------------------------------------------------
 * 20. Outranking the forms plugin
 * tw_head.php loads Tailwind with `?plugins=forms`, which styles [type='text'],
 * select and textarea at single-class specificity — and the Play CDN injects
 * its sheet after this file, so it wins the tie. That is why a text input and
 * a select sitting in the same row had different borders and padding.
 * Repeating the class outranks it without resorting to !important.
 * ------------------------------------------------------------------------- */
.ems-input.ems-input,
.ems-select.ems-select,
.ems-textarea.ems-textarea {
    padding: 6px 10px;
    border: 1px solid var(--ems-rule);
    border-radius: var(--ems-r-control);
    background-color: var(--ems-paper-2);
    color: var(--ems-ink);
    font: inherit;
    font-size: var(--ems-fs-body);
    line-height: 1.4;
    box-shadow: none;
}
.ems-select.ems-select { padding-right: 32px; }
.ems-input.ems-input:focus,
.ems-select.ems-select:focus,
.ems-textarea.ems-textarea:focus {
    outline: none;
    border-color: var(--ems-brand);
    box-shadow: var(--ems-focus);
    background-color: var(--ems-paper);
}
/* Chrome renders date and time fields in its own monospace face unless told
 * otherwise, which made them the odd ones out in every form. */
.ems-input.ems-input[type="date"],
.ems-input.ems-input[type="time"],
.ems-input.ems-input[type="datetime-local"] { font-family: inherit; }

/* ---------------------------------------------------------------------------
 * 20. Sortable table heads + tick cells
 * Report tables render their whole result set, so sorting them is a reading
 * aid, not a query: js/ems-table-sort.js reorders the rows already present.
 * The arrow is drawn on the head itself, so a sorted column stays obvious
 * after the click that sorted it.
 * ------------------------------------------------------------------------- */
.ems-tbl th.ems-sort {
    cursor: pointer;
    user-select: none;
    padding-right: 20px;
    position: relative;
}
.ems-tbl th.ems-sort:hover { color: var(--ems-brand); }
.ems-tbl th.ems-sort::after {
    content: "";
    position: absolute;
    right: 7px;
    top: 50%;
    margin-top: -5px;
    border: 4px solid transparent;
    border-top-color: var(--ems-ink-3);
    opacity: .35;
}
.ems-tbl th.ems-sort.is-asc::after {
    border-top-color: transparent;
    border-bottom-color: var(--ems-brand);
    margin-top: -9px;
    opacity: 1;
}
.ems-tbl th.ems-sort.is-desc::after {
    border-top-color: var(--ems-brand);
    opacity: 1;
}
.ems-tbl th.ems-sort:focus-visible { outline: 2px solid var(--ems-brand); outline-offset: -2px; }

/* A scan that happened is a tick, not a word: the desk reads down the column,
 * and "Yes"/"No" columns take four times the width to say the same thing. */
.ems-tick {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: 1px solid var(--ems-rule);
    border-radius: 4px;
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
    color: var(--ems-ink-3);
    background: var(--ems-paper);
}
.ems-tick--on {
    border-color: #1e7d4f;
    background: rgba(30, 125, 79, .12);
    color: #1e7d4f;
}
.ems-tick__when {
    display: block;
    margin-top: 2px;
    font-size: 10px;
    color: var(--ems-ink-3);
    font-variant-numeric: tabular-nums;
}
/* Who worked the desk, on its own line under the time. Tighter than the time
   above it so the pair reads as one caption rather than two stacked facts, and
   clipped rather than wrapped — a long username must not widen a tick column. */
.ems-tick__by {
    display: block;
    max-width: 96px;
    margin: 0 auto;
    font-size: 10px;
    line-height: 1.25;
    color: var(--ems-ink-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---------------------------------------------------------------------------
 * Menu — a button that opens a short list of related actions.
 *
 * Built on <details>/<summary> rather than JS state, so it works the moment the
 * markup lands: the participants toolbar is injected into the setup wizard by
 * innerHTML, where a component that needed an init pass would arrive dead.
 *
 * It exists because a toolbar of eight peer buttons reads as no hierarchy at
 * all. Sends belong together, import/export belong together, and the only thing
 * that deserves a filled button is Add.
 * ------------------------------------------------------------------------- */
.ems-menu { position: relative; }

/* The default disclosure triangle is not part of the button. */
.ems-menu > summary { list-style: none; cursor: pointer; }
.ems-menu > summary::-webkit-details-marker { display: none; }
.ems-menu > summary::marker { content: ''; }

.ems-menu[open] > summary          { background: var(--ems-paper-3); }
.ems-menu[open] .ems-menu__caret   { transform: rotate(180deg); }
.ems-menu__caret                   { transition: transform .15s ease; }

.ems-menu__panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 40;
    min-width: 264px;
    padding: 6px;
    background: var(--ems-paper);
    border: 1px solid var(--ems-rule);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(25, 28, 30, .14);
}
/* Opening a menu inside a narrow pane must not push the page sideways. */
@media (max-width: 640px) {
    .ems-menu__panel { right: auto; left: 0; min-width: 232px; }
}

.ems-menu__item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--ems-ink);
    font: inherit;
    font-size: 13px;
    line-height: 1.35;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.ems-menu__item:hover:not(:disabled) { background: var(--ems-paper-3); }
.ems-menu__item:disabled             { opacity: .45; cursor: default; }
.ems-menu__item .material-symbols-outlined { font-size: 20px; color: var(--ems-ink-3); }

/* The number is the point of the row, so it is not allowed to wrap or shift. */
.ems-menu__count {
    margin-left: auto;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--ems-ink-3);
}

.ems-menu__note {
    margin-top: 4px;
    padding: 7px 10px 3px;
    border-top: 1px solid var(--ems-rule-soft);
    font-size: 11px;
    line-height: 1.45;
    color: var(--ems-ink-3);
}
