/* File: TicketSys/css/user_portal.css
 *
 * The authenticated end-user portal: dashboard, tickets, requests, notifications,
 * knowledge base, announcements, feedback and profile.
 *
 * Builds on portal-shell.css (buttons, pills, queue rows) and the ECVALLA identity
 * tokens. Layout is a fixed sidebar on wide screens that becomes a bottom tab bar on
 * phones — the desktop UI is not merely shrunk: tables collapse into cards, the New
 * Ticket CTA moves from the sidebar into a floating action button, and hit targets
 * grow to touch size.
 */

/* The portal toggles visibility with the `hidden` attribute throughout. Several of its
 * layout classes set display:grid/flex, which outranks the UA's bare `display: none`
 * for [hidden] — this restores the attribute's meaning everywhere on these pages. */
[hidden] {
    display: none !important;
}

/* --- Frame ------------------------------------------------------------- */

.up-shell {
    display: grid;
    grid-template-columns: 248px minmax(0, 1fr);
    min-height: 100svh;
}

/* --- Sidebar ------------------------------------------------------------ */

.up-sidebar {
    position: sticky;
    top: 0;
    align-self: start;
    display: flex;
    flex-direction: column;
    gap: 18px;
    height: 100svh;
    padding: 22px 16px;
    background-color: var(--ecv-ink);
    color: #fff;
}

.up-sidebar .brand-wordmark {
    height: 38px;
    width: auto;
    filter: invert(1);
    mix-blend-mode: screen;
    object-position: center;
}

.up-brand {
    display: flex;
    flex-direction: column;
    gap: 3px;
    align-items: center;
    text-align: center;
    padding: 8px 12px 14px;
    border-bottom: 1px solid rgba(255, 255, 255, .12);
}

.up-brand small {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .55);
}

.up-new-ticket {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    min-height: 46px;
    border-radius: var(--ecv-radius-control);
    background-color: var(--ecv-paper, #fff);
    color: var(--ecv-ink);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .11em;
    text-transform: uppercase;
    text-decoration: none;
    transition: transform var(--ecv-quick), box-shadow var(--ecv-quick);
}

.up-new-ticket:hover {
    transform: translateY(-1px);
    box-shadow: var(--ecv-shadow-press);
}

.up-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    overflow-y: auto;
}

.up-nav a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 12px;
    border-radius: var(--ecv-radius-control);
    color: rgba(255, 255, 255, .72);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    transition: background-color var(--ecv-quick), color var(--ecv-quick);
}

.up-nav a i {
    width: 18px;
    text-align: center;
    font-size: 14px;
}

.up-nav a:hover {
    background-color: rgba(255, 255, 255, .08);
    color: #fff;
}

.up-nav a.is-active {
    background-color: rgba(255, 255, 255, .14);
    color: #fff;
}

.up-nav-badge {
    margin-inline-start: auto;
    min-width: 20px;
    padding: 2px 7px;
    border-radius: var(--ecv-radius-pill);
    background-color: var(--ecv-paper, #fff);
    color: var(--ecv-ink);
    font-size: 11px;
    font-weight: 700;
    text-align: center;
}

.up-nav-badge[hidden] {
    display: none;
}

.up-sidebar-foot {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, .12);
}

.up-sidebar-foot .up-user {
    padding: 4px 12px 10px;
    font-size: 11px;
    color: rgba(255, 255, 255, .55);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.up-foot-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 12px;
    border-radius: var(--ecv-radius-control);
    color: rgba(255, 255, 255, .72);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    transition: background-color var(--ecv-quick), color var(--ecv-quick);
}

.up-foot-link i {
    width: 18px;
    text-align: center;
}

.up-foot-link:hover,
.up-foot-link.is-active {
    background-color: rgba(255, 255, 255, .1);
    color: #fff;
}

.up-signout {
    background: none;
    border: 0;
    cursor: pointer;
    font-family: inherit;
    text-align: start;
}

/* --- Main column --------------------------------------------------------- */

.up-main {
    padding: 26px clamp(16px, 3vw, 48px) 96px;
    width: 100%;
    min-width: 0;
}

.up-page-head {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 4px;
    margin-bottom: 22px;
}

.up-page-head h1 {
    margin: 0;
    font-size: clamp(20px, 2.6vw, 28px);
    letter-spacing: -.01em;
}

.up-page-head p {
    margin: 0;
    font-size: 13px;
    color: var(--text-light);
    max-width: 60ch;
}

/* --- Cards & sections ----------------------------------------------------- */

.up-card {
    background-color: var(--bg-white);
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-card);
    padding: 20px;
}

.up-section {
    margin-top: 26px;
}

.up-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}

.up-section-head h2 {
    margin: 0;
    font-size: 15px;
}

.up-section-head a {
    font-size: 12px;
    font-weight: 700;
    color: var(--text-color);
}

/* --- KPI row --------------------------------------------------------------- */

.up-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 16px;
    justify-content: space-between;
}

.up-kpi {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 104px;
    padding: 16px 18px;
    background-color: var(--bg-white);
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-card);
    text-decoration: none;
    color: inherit;
    text-align: center;
    transition: transform var(--ecv-quick), box-shadow var(--ecv-quick), border-color var(--ecv-quick);
}

.up-kpi:hover {
    transform: translateY(-2px);
    box-shadow: var(--ecv-shadow-raise);
    border-color: var(--ecv-ink-soft);
}

.up-kpi b {
    display: block;
    width: 100%;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--text-light);
    text-align: center;
}

.up-kpi strong {
    display: block;
    width: 100%;
    margin-top: 8px;
    font-size: 30px;
    line-height: 1;
    letter-spacing: -.02em;
    text-align: center;
}

/* --- Pending actions -------------------------------------------------------- */

.up-pending {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.up-pending-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 16px;
    padding: 14px 16px;
    border: 1px solid var(--ecv-red-line);
    background-color: var(--ecv-red-wash);
    border-radius: var(--ecv-radius-card);
}

.up-pending-row .up-pending-body {
    flex: 1;
    min-width: 220px;
}

.up-pending-row b {
    font-size: 13px;
}

.up-pending-row p {
    margin: 3px 0 0;
    font-size: 12px;
    color: var(--text-light);
}

.up-allcaught {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 18px;
    border: 1px solid var(--border-color);
    background-color: var(--ecv-green-wash);
    border-radius: var(--ecv-radius-card);
    font-size: 13px;
}

.up-allcaught i {
    color: var(--ecv-green);
    font-size: 18px;
}

/* --- Ticket table / cards ----------------------------------------------------- */

.up-table-wrap {
    overflow-x: auto;
    background-color: var(--ecv-paper, var(--card-bg, #1e2124));
    border: 1px solid var(--ecv-line, var(--border-color, #e5e6e4));
    border-radius: var(--ecv-radius-card, 8px);
    scrollbar-width: thin;
}

table.up-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 12.5px;
    white-space: nowrap;
}

.up-table th {
    position: sticky;
    top: 0;
    z-index: 10;
    height: 42px;
    padding: 0 12px;
    text-align: center;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-color);
    background: var(--ecv-soft, #25282c);
    border-bottom: 1px solid var(--ecv-line, #e5e6e4);
    white-space: nowrap;
    transition: background 0.15s, color 0.15s;
}

.up-table th:hover {
    background: var(--ecv-canvas, #2d3136);
}

.up-table td {
    height: 48px;
    padding: 0 12px;
    border-bottom: 1px solid var(--ecv-line, var(--border-color, #e5e6e4));
    text-align: center;
    color: var(--text-color);
    font-size: 12.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    background: var(--ecv-paper, var(--card-bg, #1e2124));
    vertical-align: middle;
}

.up-table tbody tr:last-child td {
    border-bottom: 0;
}

.up-table tbody tr {
    cursor: pointer;
    transition: background 0.15s ease;
}

.up-table tbody tr:hover td {
    background-color: var(--ecv-soft, #25282c) !important;
}

.up-table .up-ref {
    font-weight: 700;
    white-space: nowrap;
}

.up-table .up-sub {
    max-width: 340px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- Activity feed --------------------------------------------------------------- */

.up-feed {
    display: flex;
    flex-direction: column;
}

.up-feed-item {
    display: flex;
    gap: 12px;
    padding: 12px 4px;
    border-bottom: 1px solid var(--border-color);
    font-size: 13px;
}

.up-feed-item:last-child {
    border-bottom: 0;
}

.up-feed-item i {
    margin-top: 2px;
    width: 18px;
    text-align: center;
    color: var(--text-light);
}

.up-feed-item time {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: var(--text-light);
}

.up-feed-item.is-unread {
    font-weight: 600;
}

/* --- Filters / toolbar --------------------------------------------------------------- */

.up-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
}

.up-chiprow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
}

.up-chip {
    padding: 8px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-pill);
    background-color: var(--bg-white);
    color: var(--text-color);
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: background-color var(--ecv-quick), color var(--ecv-quick), border-color var(--ecv-quick);
}

.up-chip:hover {
    border-color: var(--ecv-ink-soft);
}

.up-chip.is-active {
    background-color: var(--sys-action-primary, var(--ecv-ink));
    border-color: var(--sys-action-primary, var(--ecv-ink));
    /* Inverts with the plate: white on ink in light, ink on the light plate in dark. A
     * literal #fff here was white-on-white the moment the theme flipped. */
    color: var(--sys-action-primary-text, #fff);
}

.up-search {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 280px;
    min-width: 200px;
    max-width: 600px;
    padding: 0 14px;
    min-height: 42px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-control);
    background-color: var(--bg-white);
}

.up-search i {
    color: var(--text-light);
    font-size: 13px;
}

.up-search input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: none;
    background: none;
    font-family: inherit;
    font-size: 13px;
    color: var(--text-color);
}

.up-select {
    min-height: 42px;
    padding: 0 12px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-control);
    background-color: var(--bg-white);
    font-family: inherit;
    font-size: 12px;
    color: var(--text-color);
}

/* --- Tickets: list + detail split ------------------------------------------------------ */

.up-split {
    display: grid;
    grid-template-columns: minmax(320px, 440px) minmax(0, 1fr);
    gap: 20px;
    align-items: start;
    width: 100%;
}

.up-detail {
    position: sticky;
    top: 20px;
    width: 100%;
    box-sizing: border-box;
    background-color: var(--bg-white);
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-card);
    padding: 22px;
    min-height: 320px;
}

.up-detail-head h2 {
    margin: 4px 0 10px;
    font-size: 18px;
}

/* The facts strip: one row of label-over-value pairs, distributed across the full width.
 *
 * It was an auto-fill grid, which sized every column to the widest cell and left the
 * remainder as a ragged gap at the end — the uneven spacing in the review. Flex with
 * space-between distributes the free space BETWEEN the pairs instead, so the row reads as a
 * deliberately spaced set. Each pair is centred on its own axis, label over value, which is
 * what makes a row of them scan as a unit rather than as left-aligned fragments. */
.up-facts {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px 24px;
    margin: 16px 0;
    padding: 16px 20px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-card);
    background-color: var(--ecv-soft);
}

.up-facts div {
    min-width: 0;
    /* Each pair takes its natural width and may shrink; none may dominate the row. */
    flex: 0 1 auto;
    text-align: center;
}

.up-facts dt {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-light);
    white-space: nowrap;
}

/* Arabic is never letter-spaced or uppercased. */
html[dir="rtl"] .up-facts dt {
    letter-spacing: normal;
    text-transform: none;
    font-size: 12px;
}

.up-facts dd {
    margin: 4px 0 0;
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    font-variant-numeric: tabular-nums;
}

/* Below the point where the row would wrap awkwardly, an even grid of centred pairs keeps
 * the alignment promise without stretching two lonely items across the width. */
@media (max-width: 900px) {
    .up-facts {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
        justify-content: normal;
    }
}

/* --- Lifecycle bar ------------------------------------------------------------------------ */

.up-lifecycle {
    display: flex;
    align-items: center;
    gap: 0;
    margin: 16px 0;
    overflow-x: auto;
    padding-bottom: 4px;
}

.up-stage {
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-light);
}

.up-stage::after {
    content: '';
    width: 26px;
    height: 1px;
    margin: 0 10px;
    background-color: var(--border-color);
}

.up-stage:last-child::after {
    display: none;
}

.up-stage .dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 2px solid var(--border-color);
    background-color: var(--bg-white);
}

.up-stage.is-done { color: var(--text-color); }
.up-stage.is-done .dot { background-color: var(--ecv-ink); border-color: var(--ecv-ink); }

.up-stage.is-current { color: var(--ecv-ink); }
.up-stage.is-current .dot {
    background-color: var(--bg-white);
    border-color: var(--ecv-ink);
    box-shadow: 0 0 0 3px rgba(23, 25, 27, .12);
}

/* --- Conversation thread --------------------------------------------------------------------- */

.up-thread {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin: 20px 0;
    padding: 12px 6px;
    /* No max-height and no scrollbar of its own. The thread appears on exactly one screen —
     * the ticket page — and that page scrolls as a page. A bounded copy here would only be
     * the rule that applies in the instant before `has-ticket-page` is set, which is enough
     * to produce a scrollbar that appears and then goes away. */
}

/* Ticket detail conversation -------------------------------------------------
 *
 * The full ticket page owns the outer viewport, so the conversation is framed as a
 * card — border, radius, its own ground — but it is not a scroll container. An
 * earlier revision gave it a viewport-derived height to keep the composer near the
 * latest reply; what that actually produced was a second scroller covering most of
 * the page, which swallowed the wheel and refused to hand it back. The composer is
 * reached by scrolling the page, like everything else on it. */
body.has-ticket-page #detailBody > .up-subhead:has(+ .up-thread) {
    margin: 14px 0 8px !important;
}

body.has-ticket-page #detailBody .up-thread {
    box-sizing: border-box;
    width: 100%;
    /* The conversation grows; the PAGE scrolls. It used to be a 280–520px box with its own
     * scrollbar and `overscroll-behavior: contain`, and the render pinned it to the bottom.
     * Both together made the page feel broken: the box covers most of the ticket page, so
     * as soon as a click or the pointer landed in it the wheel scrolled the thread instead
     * of the page — and the thread was already at its end, so nothing moved and the scroll
     * did not chain out. One scroll region per view; this page's is the page. */
    height: auto;
    min-height: 0;
    max-height: none;
    margin: 0;
    padding: 12px;
    gap: 10px;
    /* `clip`, not `hidden`. A `hidden` on one axis forces the other axis's `visible` to
     * compute as `auto`, which quietly makes the element a scroll container again — the
     * exact thing being removed. `clip` leaves the block axis alone. */
    overflow-x: clip;
    overflow-y: visible;
    border: 1px solid var(--sys-border, var(--border-color, #e5e6e4));
    border-radius: 14px;
    background-color: var(--sys-surface-raised, var(--bg-white, #ffffff));
    box-shadow: 0 8px 24px rgba(23, 25, 27, .045);
}

body.has-ticket-page #detailBody .up-msg {
    gap: 9px;
    line-height: 1.48;
}

body.has-ticket-page #detailBody .up-msg.is-follow {
    margin-top: -4px;
}

body.has-ticket-page #detailBody .up-msg header {
    margin-bottom: 3px;
}

body.has-ticket-page #detailBody .up-msg-bubble {
    padding: 9px 12px;
    border-radius: 14px;
}

body.has-ticket-page #detailBody .up-msg p {
    line-height: 1.48;
}

body.has-ticket-page #detailBody .up-reply {
    gap: 8px;
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 14px;
    background-color: var(--sys-surface-raised, var(--bg-white, #ffffff));
}

body.has-ticket-page #detailBody .up-reply textarea {
    min-height: 62px;
    max-height: 120px;
    padding: 9px 11px;
    resize: vertical;
}

body.has-ticket-page #detailBody .up-reply-actions {
    gap: 8px;
}

/* On a phone the conversation keeps its own padding and radius; its HEIGHT is the
 * content's, here as everywhere, so there is nothing left to override per viewport. */
@media (max-width: 700px) {
    body.has-ticket-page #detailBody .up-thread {
        padding: 10px;
        border-radius: 12px;
    }

    body.has-ticket-page #detailBody .up-msg {
        max-width: 92%;
    }

    body.has-ticket-page #detailBody .up-reply {
        padding: 10px;
        border-radius: 12px;
    }
}

.up-msg {
    display: flex;
    gap: 12px;
    max-width: 78%;
    align-self: flex-start;
    font-size: 13.5px;
    line-height: 1.58;
    animation: up-msg-fadein 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes up-msg-fadein {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: translateY(0); }
}

.up-msg.is-follow {
    margin-top: -8px;
}

.up-msg.is-mine {
    align-self: flex-end;
    flex-direction: row-reverse;
}

/* Modern Gradient Avatar Badges with Ultra Contrast */
.up-msg-avatar {
    flex: 0 0 34px;
    width: 34px;
    height: 34px;
    margin-top: 2px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 800;
    letter-spacing: .02em;
    background: linear-gradient(135deg, #374151 0%, #1f2937 100%);
    color: #ffffff !important;
    border: 1.5px solid rgba(0, 0, 0, 0.15);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    user-select: none;
}

.up-msg.is-mine .up-msg-avatar {
    background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
    color: #ffffff !important;
    border: 1.5px solid rgba(59, 130, 246, 0.4);
    box-shadow: 0 2px 10px rgba(37, 99, 235, 0.3);
}

.up-msg-avatar.is-spacer {
    background: none;
    border: 0;
    box-shadow: none;
    visibility: hidden;
}

.up-msg-main {
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.up-msg.is-mine .up-msg-main {
    align-items: flex-end;
}

/* Header & Meta */
.up-msg header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 5px;
    font-size: 11.5px;
    color: var(--text-light, #73787b);
}

.up-msg.is-mine header {
    flex-direction: row-reverse;
}

.up-msg header b {
    color: var(--text-color, #17191b);
    font-size: 12.5px;
    font-weight: 700;
}

.up-msg header span {
    font-size: 11px;
    color: var(--text-light, #73787b);
    opacity: 0.85;
}

/* Bubbles - Left / Incoming */
.up-msg-bubble {
    position: relative;
    padding: 12px 18px;
    border-radius: 18px;
    border: 1px solid var(--border-color, #e5e6e4);
    background-color: var(--bg-white, #ffffff);
    color: var(--text-color, #17191b);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
    word-break: break-word;
    transition: box-shadow 0.15s ease;
}

.up-msg.is-first .up-msg-bubble {
    border-start-start-radius: 4px;
}

/* Bubbles - Right / Outgoing (Mine) */
.up-msg.is-mine .up-msg-bubble {
    background: linear-gradient(145deg, #17191b 0%, #202326 100%);
    border: 1px solid rgba(255, 255, 255, 0.08);
    color: #ffffff !important;
    box-shadow: 0 4px 14px rgba(23, 25, 27, 0.16);
}

.up-msg.is-mine .up-msg-bubble p {
    color: #ffffff !important;
}

.up-msg.is-mine.is-first .up-msg-bubble {
    border-start-start-radius: 18px;
    border-start-end-radius: 4px;
}

.up-msg-bubble time {
    display: block;
    margin-top: 6px;
    font-size: 10.5px;
    color: var(--text-light, #73787b);
    text-align: end;
    opacity: 0.8;
}

.up-msg.is-mine .up-msg-bubble time {
    color: rgba(255, 255, 255, 0.65) !important;
}

.up-msg p {
    margin: 0;
    font-size: 13.5px;
    line-height: 1.55;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Attachments */
.up-msg .up-msg-attachments {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

.up-msg .up-msg-attachments a,
.up-attachments a {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 12px;
    border: 1px solid var(--border-color, #e5e6e4);
    border-radius: 8px;
    background-color: var(--bg-white, #ffffff);
    font-size: 12px;
    font-weight: 600;
    text-decoration: none;
    color: var(--text-color, #17191b);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
    transition: transform 0.15s ease, border-color 0.15s ease;
}

.up-msg .up-msg-attachments a:hover,
.up-attachments a:hover {
    border-color: var(--ecv-ink, #17191b);
    transform: translateY(-1px);
}

.up-msg.is-mine .up-msg-attachments a {
    background-color: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.25);
    color: #ffffff !important;
}

.up-msg.is-mine .up-msg-attachments a:hover {
    background-color: rgba(255, 255, 255, 0.2);
    border-color: rgba(255, 255, 255, 0.4);
}

/* Modern Composer Box */
.up-reply {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 18px;
    padding: 16px 20px;
    background: var(--ecv-paper, #ffffff);
    border: 1px solid var(--border-color, #e5e6e4);
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
}

.up-reply textarea {
    width: 100%;
    min-height: 90px;
    padding: 12px 14px;
    border: 1px solid var(--border-color, #e5e6e4);
    border-radius: 8px;
    background: var(--ecv-canvas, #f5f5f3);
    color: var(--text-color, #17191b);
    font-family: inherit;
    font-size: 13.5px;
    line-height: 1.5;
    resize: vertical;
    box-sizing: border-box;
    transition: border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}

.up-reply textarea:focus {
    outline: none;
    border-color: var(--ecv-ink, #17191b);
    background: var(--ecv-paper, #ffffff);
    box-shadow: 0 0 0 3px rgba(23, 25, 27, 0.08);
}

.up-reply-foot {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
}

/* Dark Theme overrides for conversation */
html.dark-theme .up-msg-bubble,
[data-theme="dark"] .up-msg-bubble,
body.dark-theme .up-msg-bubble {
    background-color: #1e2226;
    border-color: rgba(255, 255, 255, 0.08);
    color: #e9eae8;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}

html.dark-theme .up-msg-bubble p,
[data-theme="dark"] .up-msg-bubble p,
body.dark-theme .up-msg-bubble p {
    color: #e9eae8;
}

html.dark-theme .up-msg.is-mine .up-msg-bubble,
[data-theme="dark"] .up-msg.is-mine .up-msg-bubble,
body.dark-theme .up-msg.is-mine .up-msg-bubble {
    background: linear-gradient(145deg, #2b3036 0%, #1e2125 100%);
    border-color: rgba(255, 255, 255, 0.15);
    color: #ffffff !important;
    box-shadow: 0 4px 18px rgba(0, 0, 0, 0.4);
}

html.dark-theme .up-msg.is-mine .up-msg-bubble p,
[data-theme="dark"] .up-msg.is-mine .up-msg-bubble p,
body.dark-theme .up-msg.is-mine .up-msg-bubble p {
    color: #ffffff !important;
}

html.dark-theme .up-msg-avatar,
[data-theme="dark"] .up-msg-avatar,
body.dark-theme .up-msg-avatar {
    background: linear-gradient(135deg, #475569 0%, #334155 100%) !important;
    color: #ffffff !important;
    border: 1.5px solid rgba(255, 255, 255, 0.35) !important;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.25) !important;
}

html.dark-theme .up-msg.is-mine .up-msg-avatar,
[data-theme="dark"] .up-msg.is-mine .up-msg-avatar,
body.dark-theme .up-msg.is-mine .up-msg-avatar {
    background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important;
    color: #ffffff !important;
    border: 1.5px solid rgba(147, 197, 253, 0.6) !important;
    box-shadow: 0 0 14px rgba(59, 130, 246, 0.4), 0 2px 8px rgba(0, 0, 0, 0.4) !important;
}

html.dark-theme .up-msg header b,
[data-theme="dark"] .up-msg header b,
body.dark-theme .up-msg header b {
    color: #ffffff;
}

html.dark-theme .up-msg header span,
[data-theme="dark"] .up-msg header span,
body.dark-theme .up-msg header span {
    color: rgba(255, 255, 255, 0.55);
}

html.dark-theme .up-reply,
[data-theme="dark"] .up-reply,
body.dark-theme .up-reply {
    background: #17191b;
    border-color: rgba(255, 255, 255, 0.1);
}

html.dark-theme .up-reply textarea,
[data-theme="dark"] .up-reply textarea,
body.dark-theme .up-reply textarea {
    background: #111315;
    border-color: rgba(255, 255, 255, 0.12);
    color: #ffffff;
}

html.dark-theme .up-reply textarea:focus,
[data-theme="dark"] .up-reply textarea:focus,
body.dark-theme .up-reply textarea:focus {
    border-color: rgba(255, 255, 255, 0.4);
    background: #15181b;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.08);
}

.up-reply textarea {
    width: 100%;
    min-height: 84px;
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-control);
    background-color: var(--bg-white);
    color: var(--text-color);
    font-family: inherit;
    font-size: 13px;
    resize: vertical;
}

.up-reply-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

.up-reply-files {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 12px;
    color: var(--text-light);
}

/* --- Stars ------------------------------------------------------------------------------------- */

.up-stars {
    display: inline-flex;
    gap: 4px;
    font-size: 22px;
}

.up-stars button {
    background: none;
    border: 0;
    padding: 2px;
    cursor: pointer;
    color: var(--border-color);
    transition: color var(--ecv-quick), transform var(--ecv-quick);
}

.up-stars button:hover {
    transform: scale(1.15);
}

.up-stars button.is-on {
    color: var(--ecv-ink);
}

.up-stars.is-readonly button {
    cursor: default;
    transform: none;
}

/* --- Announcements / KB / notification rows ------------------------------------------------------ */

.up-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.up-list-row {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 16px 18px;
    background-color: var(--bg-white);
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-card);
    text-align: start;
    width: 100%;
    font-family: inherit;
    color: inherit;
    cursor: pointer;
    transition: border-color var(--ecv-quick), box-shadow var(--ecv-quick);
}

.up-list-row:hover {
    border-color: var(--ecv-ink-soft);
}

.up-list-row.is-unread {
    border-inline-start: 3px solid var(--ecv-ink);
}

.up-list-row .up-row-icon {
    margin-top: 2px;
    width: 20px;
    text-align: center;
    color: var(--text-light);
}

.up-list-row .up-row-body {
    flex: 1;
    min-width: 0;
}

.up-list-row b {
    font-size: 13.5px;
}

.up-list-row p {
    margin: 4px 0 0;
    font-size: 12.5px;
    color: var(--text-light);
    line-height: 1.6;
}

.up-list-row time {
    font-size: 11px;
    color: var(--text-light);
    white-space: nowrap;
}

.up-row-open {
    max-height: 0;
    overflow: hidden;
}

.up-list-row.is-open .up-row-open {
    max-height: none;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border-color);
    white-space: pre-wrap;
    font-size: 13px;
    color: var(--text-color);
    line-height: 1.7;
}

/* --- Notification centre ---------------------------------------------------------------
 * A dedicated command surface and inbox treatment. These selectors are intentionally
 * page-scoped so announcement, knowledge-base and feedback rows retain their own rhythm. */

.up-notifications-main {
    max-width: 1640px;
    margin-inline: auto;
}

.up-notification-head {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    min-height: 82px;
    margin-bottom: 18px;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--border-color);
}

.up-notification-heading {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
}

.up-notification-heading-icon {
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border-color);
    border-radius: 12px;
    background: color-mix(in srgb, var(--bg-white) 88%, var(--text-color) 12%);
    color: var(--text-color);
    box-shadow: 0 10px 28px -22px var(--text-color);
}

.up-notification-heading-icon i {
    font-size: 16px;
}

.up-notification-head h1 {
    font-size: clamp(23px, 2.4vw, 32px);
}

.up-notification-head p {
    margin-top: 3px;
}

.up-mark-read {
    flex: 0 0 auto;
    min-height: 40px;
    padding-inline: 16px;
    background-color: var(--bg-white);
    box-shadow: 0 8px 24px -22px var(--text-color);
}

.up-mark-read:not(:disabled):hover {
    transform: translateY(-1px);
    box-shadow: 0 12px 28px -20px var(--text-color);
}

.up-notification-toolbar {
    justify-content: space-between;
    margin-bottom: 14px;
    padding: 5px;
    border: 1px solid var(--border-color);
    border-radius: 12px;
    background-color: color-mix(in srgb, var(--bg-white) 94%, var(--text-color) 6%);
}

.up-notification-toolbar .up-chiprow {
    width: auto;
    justify-content: flex-start;
    gap: 4px;
}

.up-notification-toolbar .up-chip {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    min-height: 36px;
    padding: 7px 12px;
    border-color: transparent;
    background: transparent;
}

.up-notification-toolbar .up-chip.is-active {
    background-color: var(--sys-action-primary, var(--ecv-ink));
    border-color: var(--sys-action-primary, var(--ecv-ink));
    box-shadow: 0 7px 18px -14px var(--text-color);
}

.up-chip-count {
    min-width: 22px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-inline: 6px;
    border-radius: var(--ecv-radius-pill);
    background-color: color-mix(in srgb, currentColor 12%, transparent);
    font-size: 10px;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.up-notification-list {
    gap: 8px;
}

.up-notification-row {
    position: relative;
    display: grid;
    grid-template-columns: 42px minmax(0, 1fr) auto;
    align-items: center;
    gap: 14px;
    min-height: 76px;
    padding: 14px 16px;
    overflow: hidden;
    border-color: color-mix(in srgb, var(--border-color) 88%, transparent);
    box-shadow: 0 12px 30px -32px var(--text-color);
    transition: transform var(--ecv-quick), border-color var(--ecv-quick),
        background-color var(--ecv-quick), box-shadow var(--ecv-quick);
}

.up-notification-row:hover {
    transform: translateY(-1px);
    border-color: color-mix(in srgb, var(--text-color) 28%, var(--border-color));
    box-shadow: 0 18px 36px -30px var(--text-color);
}

.up-notification-row:focus-visible {
    outline: 2px solid var(--text-color);
    outline-offset: 2px;
}

.up-notification-row.is-unread {
    border-inline-start: 3px solid var(--text-color);
    background-color: color-mix(in srgb, var(--bg-white) 94%, var(--text-color) 6%);
}

.up-notification-icon {
    width: 42px;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
    border-radius: 12px;
    background-color: color-mix(in srgb, currentColor 9%, var(--bg-white));
    color: var(--text-light);
}

.up-notification-icon .up-row-icon {
    width: auto;
    margin: 0;
    color: inherit;
    font-size: 15px;
}

.up-notification-row.is-success .up-notification-icon { color: var(--ecv-green, #18864b); }
.up-notification-row.is-warning .up-notification-icon { color: #b36a00; }
.up-notification-row.is-error .up-notification-icon { color: var(--ecv-red, #b42318); }

.up-notification-row .up-row-body b {
    display: block;
    font-size: 13.5px;
    line-height: 1.45;
    letter-spacing: -.005em;
}

.up-notification-row .up-row-body p {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: 5px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .015em;
}

.up-notification-row .up-row-body p i {
    font-size: 9px;
    transition: transform var(--ecv-quick);
}

.up-notification-row:hover .up-row-body p i {
    transform: translateX(3px);
}

html[dir="rtl"] .up-notification-row .up-row-body p i {
    transform: scaleX(-1);
}

html[dir="rtl"] .up-notification-row:hover .up-row-body p i {
    transform: scaleX(-1) translateX(3px);
}

.up-notification-meta {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 9px;
    min-width: 72px;
}

.up-notification-meta time {
    font-size: 10.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.up-unread-dot {
    width: 7px;
    height: 7px;
    flex: 0 0 7px;
    border-radius: 50%;
    background-color: var(--text-color);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--text-color) 10%, transparent);
}

@media (max-width: 700px) {
    .up-notification-head {
        align-items: stretch;
        flex-direction: column;
        gap: 16px;
    }

    .up-mark-read {
        width: 100%;
    }

    .up-notification-toolbar .up-chiprow,
    .up-notification-toolbar .up-chip {
        flex: 1;
    }

    .up-notification-toolbar .up-chip {
        justify-content: center;
    }

    .up-notification-row {
        grid-template-columns: 42px minmax(0, 1fr);
        align-items: start;
    }

    .up-notification-meta {
        grid-column: 2;
        justify-content: flex-start;
        min-width: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .up-notification-row,
    .up-mark-read,
    .up-notification-row .up-row-body p i {
        transition: none;
    }

    .up-notification-row:hover,
    .up-mark-read:not(:disabled):hover {
        transform: none;
    }
}

/* --- Forms (profile) -------------------------------------------------------------------------------- */

.up-form {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 16px;
}

.up-field label {
    display: block;
    margin-bottom: 6px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--text-light);
}

/* textarea belongs in this list. Left out, it fell back to its intrinsic `cols` width —
 * about twenty characters — so every multi-line field outside a modal (the notification
 * templates on Content are the visible case) rendered as a small box in a wide column,
 * while the single-line field above it spanned the full width. */
.up-field input,
.up-field select,
.up-field textarea {
    width: 100%;
    min-height: 44px;
    padding: 0 13px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-control);
    background-color: var(--bg-white);
    color: var(--text-color);
    font-family: inherit;
    font-size: 13px;
}

/* A textarea takes the same frame but not the single-line metrics: it needs padding on
 * the block axis so the first line is not against the border, room for the rows it
 * declares, and a grab handle on the vertical axis only — horizontal resizing would
 * break it out of the grid column it sits in. */
.up-field textarea {
    min-height: 96px;
    padding: 11px 13px;
    line-height: 1.55;
    resize: vertical;
}

.up-field input:read-only {
    background-color: var(--ecv-soft);
    color: var(--text-light);
}

.up-check {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
}

.up-check input {
    width: 18px;
    height: 18px;
    accent-color: var(--ecv-ink);
}

/* --- States ---------------------------------------------------------------------------------------------- */

.up-empty {
    padding: 36px 20px;
    text-align: center;
    color: var(--text-light);
    font-size: 13px;
    border: 1px dashed var(--border-color);
    border-radius: var(--ecv-radius-card);
}

.up-empty i {
    display: block;
    margin-bottom: 10px;
    font-size: 22px;
}

.up-error {
    padding: 14px 16px;
    border: 1px solid var(--ecv-red-line);
    background-color: var(--ecv-red-wash);
    border-radius: var(--ecv-radius-card);
    color: var(--ecv-red);
    font-size: 13px;
}

.up-skeleton {
    border-radius: var(--ecv-radius-card);
    background: linear-gradient(90deg, var(--ecv-soft) 25%, var(--bg-white) 50%, var(--ecv-soft) 75%);
    background-size: 200% 100%;
    animation: up-shimmer 1.4s infinite;
    min-height: 74px;
}

@keyframes up-shimmer {
    to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .up-skeleton { animation: none; }
    .up-kpi:hover, .up-new-ticket:hover, .up-stars button:hover { transform: none; }
}

.up-toast {
    position: fixed;
    bottom: 22px;
    left: 50%;
    transform: translateX(-50%) translateY(8px);
    padding: 12px 20px;
    border-radius: var(--ecv-radius-control);
    background-color: var(--ecv-ink);
    color: #fff;
    font-size: 13px;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ecv-quick), transform var(--ecv-quick);
    /* Above every overlay in the system, deliberately.
     *
     * At 60 it sat UNDER the modal backdrop (100) and under the confirm dialog above that, so
     * the one message telling you whether your save worked was hidden behind the dialog you
     * had just saved from. A toast is the outcome of an action; it has to be visible from
     * wherever that action was taken, and a modal is where most of them are taken.
     *
     * 10050 clears the highest overlay currently in use (10000). */
    z-index: 10050;
}

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

/* --- Mobile: bottom tab bar + FAB ---------------------------------------------------------------------------- */

.up-fab {
    display: none;
}

@media (max-width: 920px) {
    .up-shell {
        grid-template-columns: 1fr;
    }

    .up-sidebar {
        position: fixed;
        inset: auto 0 0 0;
        z-index: 40;
        height: auto;
        flex-direction: row;
        align-items: center;
        gap: 0;
        padding: 6px 6px calc(6px + env(safe-area-inset-bottom));
    }

    /* On a phone the brand, the CTA and the footer leave the bar: the CTA becomes the
     * FAB, sign-out lives in Profile, and the nav itself becomes a five-tab bar. */
    .up-brand,
    .up-new-ticket,
    .up-sidebar-foot .up-user,
    .up-nav a span.up-nav-label-long {
        display: none;
    }

    .up-nav {
        flex-direction: row;
        flex: 1;
        justify-content: space-around;
        overflow: visible;
    }

    .up-nav a {
        flex-direction: column;
        gap: 4px;
        padding: 8px 6px;
        font-size: 10px;
        position: relative;
    }

    .up-nav a i {
        font-size: 16px;
    }

    /* Secondary destinations collapse out of the tab bar; they remain reachable from
     * the dashboard sections that reference them. */
    .up-nav a.up-nav-secondary {
        display: none;
    }

    .up-nav-badge {
        position: absolute;
        top: 2px;
        inset-inline-end: 12%;
        margin: 0;
        min-width: 16px;
        padding: 1px 5px;
        font-size: 9px;
    }

    .up-sidebar-foot {
        border-top: 0;
        padding-top: 0;
        flex-direction: row;
    }

    .up-sidebar-foot a.up-nav-profile {
        flex-direction: column;
        gap: 4px;
        padding: 8px 10px;
        font-size: 10px;
    }

    .up-signout {
        display: none;
    }

    .up-main {
        padding-bottom: 150px;
    }

    .up-fab {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        position: fixed;
        inset-inline-end: 16px;
        bottom: calc(74px + env(safe-area-inset-bottom));
        z-index: 41;
        min-height: 52px;
        padding: 0 20px;
        border-radius: var(--ecv-radius-pill);
        background-color: var(--ecv-ink);
        color: #fff;
        font-size: 12px;
        font-weight: 700;
        letter-spacing: .1em;
        text-transform: uppercase;
        text-decoration: none;
        box-shadow: var(--ecv-shadow-float);
    }

    .up-split {
        grid-template-columns: 1fr;
    }

    /* List/detail becomes two screens: pick from the list, the detail takes over. */
    .up-split.is-detail-open .up-list-pane { display: none; }
    .up-split:not(.is-detail-open) .up-detail-pane { display: none; }

    .up-detail {
        position: static;
    }

    /* Wide ticket tables become cards. */
    .up-table thead { display: none; }

    .up-table,
    .up-table tbody,
    .up-table tr,
    .up-table td { display: block; }

    .up-table tr {
        padding: 10px 4px;
        border-bottom: 1px solid var(--border-color);
    }

    .up-table td {
        padding: 3px 12px;
        border-bottom: 0;
    }

    .up-table td[data-label]::before {
        content: attr(data-label);
        display: inline-block;
        min-width: 96px;
        font-size: 10px;
        font-weight: 700;
        letter-spacing: .14em;
        text-transform: uppercase;
        color: var(--text-light);
    }
}

/* --- New Ticket modal ---------------------------------------------------------------------------------------- */

.up-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 18px;
    background-color: rgba(23, 25, 27, .5);
    backdrop-filter: blur(2px);
}

.up-modal-backdrop[hidden] {
    display: none;
}

.up-modal {
    width: min(640px, 100%);
    max-height: min(86svh, 780px);
    display: flex;
    flex-direction: column;
    background-color: var(--bg-white);
    border-radius: var(--ecv-radius-modal);
    box-shadow: var(--ecv-shadow-float);
    overflow: hidden;
}

/* Group then category, on the .up-modal-wide dialog the ticket-type editor already defines
 * further down this file — one wide-dialog width in the portal, not two. The category
 * carries the longer names, so it takes the larger share of the row instead of an equal
 * one: an even split leaves the group padded and the category clipped. */
.up-modal-row.up-row-type {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
}

.up-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 22px;
    border-bottom: 1px solid var(--border-color);
}

.up-modal-head h2 {
    margin: 0;
    font-size: 16px;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* The close control is sized, not left to its glyph. 6px of padding around a 16px times sign
 * measured 22x34 — under this project's own 32px floor at every width, mouse included, for the
 * one control somebody reaches for when they want out of a dialog. */
.up-modal-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    min-height: 32px;
    background: none;
    border: 0;
    padding: 6px;
    font-size: 16px;
    color: var(--text-light);
    cursor: pointer;
}

.up-modal-close:hover {
    color: var(--text-color);
}

.up-modal-body {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
    padding: 20px 22px;
    overflow-x: hidden;
    overflow-y: auto;
}

.up-modal-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    min-width: 0;
}

/* The body is the modal's vertical scroll container. Its edge-to-edge footer uses
 * negative inline margins, so without an explicit x-axis policy browsers also expose
 * a horizontal scrollbar. Keep every field shrinkable and break long attachment names
 * instead of allowing them to widen the dialog. */
.up-modal-body > *,
.up-modal-row > *,
.up-modal-body .up-field,
.up-modal-body .up-reply-actions,
.up-modal-body .up-reply-files {
    min-width: 0;
    max-width: 100%;
}

.up-modal-body .up-reply-files {
    overflow-wrap: anywhere;
}

.up-modal-body textarea {
    width: 100%;
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-control);
    background-color: var(--bg-white);
    color: var(--text-color);
    font-family: inherit;
    font-size: 13px;
    resize: vertical;
}

/* Action bar: its own surface, hairline-separated, actions anchored bottom-right with
 * the primary in the corner — the resting place of the eye at the end of the form. */
.up-modal-foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 10px;
    max-width: none;
    flex-shrink: 0;
    padding: 16px 22px;
    border-top: 1px solid var(--border-color);
    background-color: color-mix(in srgb, var(--bg-white) 96%, var(--ecv-ink) 4%);
}

/* The bleed — negative margins that carry the bar out to the modal's edges — belongs ONLY to
 * a footer written INSIDE the padded body, which is where most of these live. The department
 * editor puts its footer next to the body instead, as a direct child of the modal, and there
 * the same numbers made the bar 44px wider than the dialog and pushed it 22px past both
 * corners: the buttons ended up overhanging the modal rather than sitting in it.
 *
 * Scoping the bleed to the nested case fixes that one without touching the ones that were
 * already right — a direct child needs no correction, because the modal has no padding for it
 * to escape. */
.up-modal-body .up-modal-foot {
    width: calc(100% + 44px);
    margin: 8px -22px -20px;
}

/* The bar is sized by its buttons rather than the other way round: height: auto with real
 * padding, so a label that wraps in Arabic grows the bar instead of spilling out of it. The
 * 110px floor keeps a lone "Save" from looking clipped; max-content stops three buttons —
 * Delete, Cancel, Save — from insisting on 330px inside a dialog narrower than that, which is
 * what put them on top of each other. They wrap onto a second line instead (flex-wrap on the
 * bar above), and the bar takes the height it needs. */
.up-modal-foot .dept-btn {
    min-width: min(110px, 100%);
    max-width: 100%;
    min-height: 0;
    height: auto;
    padding: 11px 18px;
    line-height: 1.2;
    white-space: nowrap;
    justify-content: center;
    flex: 0 1 auto;
}

.up-modal-foot .dept-btn-primary {
    box-shadow: 0 8px 22px -10px rgba(23, 25, 27, .55);
    transition: transform var(--ecv-quick), box-shadow var(--ecv-quick);
}

.up-modal-foot .dept-btn-primary:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 12px 26px -10px rgba(23, 25, 27, .6);
}

@media (max-width: 560px) {
    .up-modal-backdrop {
        align-items: flex-end;
        padding: 0;
    }

    /* Bottom sheet on phones: full width, rounded top only, footer buttons stacked. */
    .up-modal {
        width: 100%;
        max-height: 92svh;
        border-radius: var(--ecv-radius-modal) var(--ecv-radius-modal) 0 0;
    }

    .up-modal-row {
        grid-template-columns: 1fr;
    }

    /* Bottom sheet: full-width actions, primary on top where the thumb rests. */
    .up-modal-foot {
        flex-direction: column-reverse;
        flex-wrap: nowrap;
    }

    /* The bleed, again only where the footer is inside the padded body. */
    .up-modal-body .up-modal-foot {
        margin: 8px -22px calc(-20px + env(safe-area-inset-bottom, 0px));
    }

    .up-modal > .up-modal-foot {
        padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    }

    .up-modal-foot .dept-btn {
        width: 100%;
    }
}

.up-modal-sm {
    width: min(420px, 100%);
}

/* --- Tables: centred cells and sortable headers -------------------------------------------- */

.up-table th,
.up-table td {
    text-align: center;
    vertical-align: middle;
}

.up-table th[data-sort] {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.up-table th[data-sort] .up-sort-icon {
    margin-inline-start: 6px;
    font-size: 10px;
    opacity: .45;
}

.up-table th[data-sort].is-sorted .up-sort-icon {
    opacity: 1;
}

/* A header that sorts by a real <button>: reachable by Tab, operable by Enter and Space, and
   filling the cell so the whole header is the hit area rather than just the word. The header's
   own padding moves onto the button for exactly that reason. */

.up-table th[data-sort]:has(.up-sort) {
    padding: 0;
}

.up-table th[data-sort] .up-sort {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    min-height: 42px;
    padding-inline: 12px;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    white-space: nowrap;
    cursor: pointer;
}

.up-table th[data-sort] .up-sort:focus-visible {
    outline: 2px solid var(--text-color, #17191b);
    outline-offset: -2px;
}

.up-table th[data-sort] .up-sort:hover .up-sort-icon {
    opacity: .8;
}

/* The sorted column stays marked after the pointer leaves. An underline, not a tint: the
   header's own background is already a hover signal, and in the sticky admin table it is a
   third colour again — a rule drawn under the label reads the same in every one of them, in
   both themes, without inventing a colour. */
.up-table th[data-sort].is-sorted {
    color: var(--text-color);
    box-shadow: inset 0 -2px 0 var(--text-color);
}

/* --- Branded content loader ---------------------------------------------------------------------
 * Covers the content pane only — the sidebar stays put — while a tab's content loads.
 * The mark breathes; the track beneath it sweeps. Pure greyscale, calm, no spinner. */

.up-main {
    position: relative;
}

.up-content-loading {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 244px;
    inset-inline-end: 0;
    z-index: 999;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 22px;
    background-color: var(--ecv-canvas, #f4f4f2);
    opacity: 1;
    transition: opacity .3s ease;
    pointer-events: all;
}

@media (max-width: 920px) {
    .up-content-loading {
        inset-inline-start: 68px;
    }
}

.up-content-loading.is-done {
    opacity: 0;
    pointer-events: none;
}

.up-loading-mark {
    width: 220px;
    max-width: 80vw;
    height: auto;
    display: block;
    animation: up-loading-breathe 2.2s ease-in-out infinite;
}

@keyframes up-loading-breathe {

    0%,
    100% {
        transform: scale(.985);
    }

    50% {
        transform: scale(1.015);
    }
}

.up-loading-track {
    position: relative;
    width: 216px;
    height: 2px;
    overflow: hidden;
    border-radius: 2px;
    background-color: rgba(23, 25, 27, .1);
}

.up-loading-track span {
    position: absolute;
    inset-block: 0;
    width: 40%;
    border-radius: 2px;
    background-color: var(--ecv-ink);
    animation: up-loading-sweep 1.4s cubic-bezier(.45, 0, .2, 1) infinite;
}

@keyframes up-loading-sweep {
    0% {
        inset-inline-start: -40%;
    }

    100% {
        inset-inline-start: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {

    .up-loading-mark,
    .up-loading-track span {
        animation: none;
    }

    .up-loading-mark {
        opacity: 1;
    }
}

/* --- Language control ---------------------------------------------------------------------------
 * Fixed to the top corner of every screen so it is reachable from any page, and positioned
 * with a logical inset so Arabic moves it to the mirrored corner automatically. */

.up-langbar {
    position: fixed;
    top: 14px;
    inset-inline-end: 18px;
    z-index: 55;
}

.i18n-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--ecv-radius-pill);
    background-color: var(--bg-white);
    color: var(--text-color);
    cursor: pointer;
    box-shadow: 0 2px 10px rgba(23, 25, 27, .06);
    transition: border-color var(--ecv-quick), box-shadow var(--ecv-quick);
}

.i18n-toggle:hover {
    border-color: var(--ecv-ink-soft);
    box-shadow: 0 4px 14px rgba(23, 25, 27, .1);
}

.i18n-toggle i {
    font-size: 12px;
    color: var(--text-light);
}

/* On phones the sidebar becomes a bottom tab bar, so the control keeps the top corner
 * but shrinks to the icon plus a compact label. */
@media (max-width: 860px) {
    .up-langbar {
        top: 10px;
        inset-inline-end: 12px;
    }

    .i18n-toggle {
        padding: 6px 11px;
        font-size: 12px;
    }
}

/* Section sub-headings inside the ticket detail. Arabic is never letter-spaced, so the
 * tracking is applied to the Latin case only. */
.up-subhead {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .14em;
    color: var(--text-light);
}

[dir="rtl"] .up-subhead {
    letter-spacing: 0;
    text-transform: none;
    font-size: 12.5px;
}

/* --- Right-to-left mirroring ---------------------------------------------------------------------
 *
 * Layout mirrors itself: the shell is CSS Grid and the rows are flexbox, both of which follow
 * the document's writing direction, and every offset in this sheet is a logical inset. What
 * does NOT mirror automatically is artwork — an arrow glyph keeps pointing the same way no
 * matter the direction, so anything that means "forward" or "back" is flipped here.
 *
 * Only directional icons are listed. Icons that carry no direction (bell, star, ticket,
 * paperclip, globe) must not be flipped, or they simply look wrong. */

[dir="rtl"] .fa-arrow-left,
[dir="rtl"] .fa-arrow-right,
[dir="rtl"] .fa-arrow-right-from-bracket,
[dir="rtl"] .fa-arrow-right-to-bracket,
[dir="rtl"] .fa-chevron-left,
[dir="rtl"] .fa-chevron-right,
[dir="rtl"] .fa-angle-left,
[dir="rtl"] .fa-angle-right,
[dir="rtl"] .fa-reply,
[dir="rtl"] .fa-paper-plane,
[dir="rtl"] .fa-arrow-trend-up {
    transform: scaleX(-1);
}

/* "View all →" style links. The arrow is a literal character in the markup, and Unicode
 * does not mirror it, so it is flipped as artwork. */
[dir="rtl"] .up-section-head a,
[dir="rtl"] .up-row-open a {
    unicode-bidi: plaintext;
}

[dir="rtl"] .up-section-head a::after {
    content: '';
}

/* Progress dots read left-to-right as a sequence; in Arabic the sequence starts at the
 * right, which flexbox already does. The connector line between them is drawn with a
 * logical margin so it follows. */
[dir="rtl"] .up-lifecycle {
    flex-direction: row;
}

/* Numbers, ticket references and dates stay left-to-right inside Arabic text: "LB-4K7X2P"
 * must never be reordered, and a date must not have its parts rearranged. */
[dir="rtl"] .up-ref,
[dir="rtl"] .up-kpi strong,
[dir="rtl"] .queue-row b,
[dir="rtl"] time,
[dir="rtl"] code {
    direction: ltr;
    unicode-bidi: embed;
    display: inline-block;
}

/* The email in the sidebar footer is an identifier, not prose. */
[dir="rtl"] .up-user {
    direction: ltr;
    text-align: end;
}

/* Arabic labels are longer than their English equivalents; the filter chips and KPI
 * captions get a little more room so nothing clips. */
[dir="rtl"] .up-chip {
    padding-inline: 15px;
}

[dir="rtl"] .up-kpi b {
    font-size: 11.5px;
}


/* ------------------------------------------------------------------ *
 * Modal: the wide variant, its titled head, and the working state
 *
 * The ticket-type editor was an .adm-drawer — a panel sliding in from the edge, which is the
 * right shape for a list you dip into and the wrong one for a form with a field builder in
 * it. It is a dialog now, on the same .up-modal the department editor uses, so the two
 * editors in the same portal stop being two different kinds of object.
 * ------------------------------------------------------------------ */

.up-modal-wide {
    width: min(780px, 100%);
}

/* The head's identity block: a marked icon, a title, and the thing being edited beneath it. */
.up-modal-title {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.up-modal-mark {
    display: inline-grid;
    place-items: center;
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: var(--sys-action-primary, var(--ecv-ink, #17191b));
    color: var(--sys-action-primary-text, #ffffff);
    font-size: 14px;
}

.up-modal-title h2 {
    margin: 0;
    font-size: 16px;
    line-height: 1.25;
    letter-spacing: -.015em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.up-modal-title small {
    display: block;
    margin-top: 2px;
    font-size: 11.5px;
    line-height: 1.3;
    color: var(--text-light);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The working state. Covers the dialog and nothing else: the write is happening here, and a
 * page-wide overlay would hide the form the person is waiting on. The surface is the modal's
 * own, at high opacity rather than a wash, so the half-read form underneath does not compete
 * with the message. */
.up-modal-busy {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    background: color-mix(in srgb, var(--bg-white) 92%, transparent);
    border-radius: inherit;
}

@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .up-modal-busy { background: var(--bg-white); }
}

.up-modal-busy[hidden] {
    display: none;
}

.up-modal-busy p {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-color);
}

.up-modal-spinner {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: 2.5px solid var(--border-color);
    border-top-color: var(--sys-action-primary, var(--ecv-ink, #17191b));
    animation: up-modal-spin .8s linear infinite;
}

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

/* A spinner that cannot spin says nothing, so it becomes a pulse instead of a still ring. */
@media (prefers-reduced-motion: reduce) {
    .up-modal-spinner {
        animation: up-modal-pulse 1.4s ease-in-out infinite;
    }

    @keyframes up-modal-pulse {
        0%, 100% { opacity: 1; }
        50% { opacity: .4; }
    }
}

/* The busy layer is absolutely positioned against the dialog, so the dialog must be its
 * containing block. .up-modal already clips its corners; this gives it the origin too. */
.up-modal {
    position: relative;
}

/* --- Lifecycle bar: centred, with RAG tones ------------------------------------------------- *
 *
 * Two changes.
 *
 * CENTRED. The bar was left-aligned in a full-width panel, so on a wide screen the stages sat
 * in the left third with the rest of the row empty. It is a summary of where one ticket stands,
 * not a list — it belongs on the centre line of the panel it heads.
 *
 * `overflow-x: auto` is gone with it: a progress bar that has to be scrolled sideways to see
 * the end is not telling you where you are. Below the width where five stages fit, it wraps
 * onto two centred rows instead.
 *
 * RAG. The tone comes from the stage the ticket is AT — red while nobody has picked it up,
 * amber while it is in flight, green once it is finished. Passed stages are green because they
 * are complete. The tone is set in my_tickets.js, which is where the meaning of each stage
 * lives; this file only says what each tone looks like.
 *
 * Every colour here is a token, so both themes get the lifted variants automatically — the
 * light-mode reds and greens are too dark to read on an ink surface, and vice versa.
 * ------------------------------------------------------------------------------------------- */

.up-lifecycle {
    flex-wrap: wrap;
    justify-content: center;
    overflow-x: visible;
    row-gap: 10px;
}

/* The connector after the last stage of a wrapped row would dangle into empty space. */
.up-stage:last-child::after {
    display: none;
}

/* --- Tones --- */

.up-stage[data-tone="red"] { color: var(--ecv-red, #ad291f); }

.up-stage[data-tone="red"] .dot {
    background-color: var(--ecv-red, #ad291f);
    border-color: var(--ecv-red, #ad291f);
}

.up-stage[data-tone="amber"] { color: var(--ecv-amber, #8a5d00); }

.up-stage[data-tone="amber"] .dot {
    background-color: var(--ecv-amber, #8a5d00);
    border-color: var(--ecv-amber, #8a5d00);
}

.up-stage[data-tone="green"] { color: var(--ecv-green, #13813b); }

.up-stage[data-tone="green"] .dot {
    background-color: var(--ecv-green, #13813b);
    border-color: var(--ecv-green, #13813b);
}

/* A completed stage is green, but quieter than the one the ticket is actually at — otherwise
 * four finished stages shout as loudly as the one place the reader is looking for. */
.up-stage.is-done[data-tone="green"] {
    color: var(--ecv-green, #13813b);
    opacity: .72;
}

/* The current stage keeps the ring that marks it as "here", now in its own tone. The ring is
 * what makes the bar readable without colour at all. */
.up-stage.is-current[data-tone="red"] .dot { box-shadow: 0 0 0 3px var(--ecv-red-wash, rgba(173, 41, 31, .18)); }
.up-stage.is-current[data-tone="amber"] .dot { box-shadow: 0 0 0 3px var(--ecv-amber-wash, rgba(176, 122, 12, .18)); }
.up-stage.is-current[data-tone="green"] .dot { box-shadow: 0 0 0 3px var(--ecv-green-wash, rgba(19, 129, 59, .18)); }

.up-stage.is-current { opacity: 1; }

/* The connector between a completed stage and the next one shows the path already travelled. */
.up-stage.is-done::after {
    background-color: var(--ecv-green-line, rgba(19, 129, 59, .32));
}

/* --- Reported + attachments, one row -------------------------------------------------------- *
 * Both are short facts about the ticket. Stacking them pushed the conversation — the part people
 * open a ticket to read — below the fold, for two blocks that together fill half a line.
 * They collapse back to a stack below the width where two columns stop being readable. */

.up-detail-facts {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: 18px;
    align-items: start;
    margin-bottom: 6px;
}

.up-detail-facts > section { min-width: 0; }

.up-detail-facts .up-attachments {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0;
}

.up-detail-facts .dept-description { margin-bottom: 0; }

@media (max-width: 820px) {
    .up-detail-facts { grid-template-columns: 1fr; gap: 12px; }
}

/* The conversation avatar is now the shared component (shared/js/avatar.js), so the bubble's
   own circle styling steps aside and only the layout it contributes remains. Without this the
   old background and font-size fought the component and a photograph rendered inside a
   coloured ring. */
.up-msg-avatar.pav {
    background: var(--pav-bg, #4a5568);
}

.up-msg-avatar.is-spacer {
    visibility: hidden;
}
