@import '/shared/css/portal-chrome.css';

/* File: portal-user/css/user-portal-identity.css
 *
 * The Support portal's own look.
 *
 * This portal is for everybody at ECVALLA, most of whom open it once a month with a
 * problem. It is therefore the lightest and most spacious of the three: warm off-white
 * ground, white cards, a generous type scale, and a single obvious action on every screen.
 * Nothing is dense, because nobody here is doing this all day.
 *
 * It shares ECVALLA's palette and typefaces with the other two portals — one company, one
 * identity, and colour still only ever carries meaning — and shares nothing else. The
 * hero, the navigation, the composition and the rhythm are this portal's alone: the
 * department desk is a dark operational band, administration is a framed instrument panel,
 * and neither would be right here.
 *
 * Loads after ecvalla-identity.css and portal-auth.css.
 */

.up-portal-user {
    background: var(--ecv-canvas, #f5f5f3);
    color: var(--ecv-ink, #17191b);
    margin: 0;
}

/* ================================================================== *
 * Home
 *
 * The front door is one full viewport of statement, then five bands that answer the
 * questions somebody asks before their first request: how it works, where it goes, what is
 * inside, what people ask, and where to start.
 *
 * Three rules hold this section together:
 *
 *   1. Every colour is mixed from two channel tokens — --u-ink and --u-paper — which are
 *      redefined once for dark mode. Nothing here names a hex value twice, so there is no
 *      surface dark mode cannot reach. (The one exception is --u-ok, the resolved green,
 *      which is a meaning rather than a shade and so has its own channel.)
 *   2. Motion is decoration, never information. Every animation is a transform or an
 *      opacity, every one of them is switched off by prefers-reduced-motion, and no content
 *      is hidden behind a scroll position — the reveal classes start visible and are only
 *      taken down by portal-user/js/home-motion.js once it knows it can put them back.
 *   3. Logical properties throughout, because the whole page reverses in Arabic.
 * ================================================================== */

.uhome {
    /* Colour channels, so every wash below can be mixed rather than hard-coded. */
    --u-ink: 23 25 27;
    --u-paper: 255 255 255;
    --u-ok: 19 129 59;

    /* Geometry. --u-bar is .pbar's min-height in portal-chrome.css: the hero fills exactly
       what the bar leaves behind, and --u-max is deliberately uncapped — this page uses the
       whole width of the screen, with the side padding as its only margin. */
    --u-bar: 66px;
    --u-max: none;
    --u-band: clamp(58px, 8vh, 112px);
    --u-ease: cubic-bezier(.16, 1, .3, 1);

    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

:root.dark-theme .uhome,
:root[data-theme="dark"] .uhome {
    --u-ink: 233 234 232;
    --u-paper: 24 26 28;
    --u-ok: 111 207 149;
}

/* ------------------------------------------------------------------ *
 * Hero — one viewport, edge to edge
 * ------------------------------------------------------------------ */

/* The backdrop is not mounted here any more. It is page-level and fixed to the viewport, so
   the hero and every band below it scroll over one still surface rather than each carrying
   its own. This section stays positioned and isolated for the scroll cue it holds. */
.uhero {
    position: relative;
    isolation: isolate;
    display: flex;
    align-items: center;
    box-sizing: border-box;
    min-height: calc(100svh - var(--u-bar));
    padding: clamp(24px, 4vh, 56px) clamp(20px, 3.2vw, 72px) clamp(76px, 9vh, 108px);
    overflow: hidden;
}

.uhero-inner {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr);
    align-items: center;
    gap: clamp(28px, 4vw, 72px);
    width: 100%;
    max-width: var(--u-max);
    margin: 0 auto;
}

.uhero-copy {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    max-width: min(1000px, 100%);
}

/* The entrance. Each block arrives a beat after the one above it; the two session-dependent
   rows are hidden at load, so their beat simply starts when home.js reveals them. */
.uhero-copy > *,
.uhero-art {
    animation: u-rise .85s var(--u-ease) both;
}

.uhero-eyebrow { animation-delay: .04s; }
.uhero h1 { animation-delay: .10s; }
.uhero-lede { animation-delay: .17s; }
.uhero-actions { animation-delay: .24s; }
.uhero-pills { animation-delay: .30s; }
.uhero-note { animation-delay: .34s; }
.uhero-assure { animation-delay: .40s; }
.uhero-art { animation-delay: .18s; }

.uhero-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    align-self: flex-start;
    margin: 0 0 14px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--sys-text-secondary, #73787b);
}

.uhero-eyebrow::before {
    content: "";
    width: 28px;
    height: 2px;
    background: currentColor;
}

html[dir="rtl"] .uhero-eyebrow {
    letter-spacing: normal;
    text-transform: none;
    font-size: 13px;
}

.uhero h1 {
    margin: 0 0 16px;
    font-size: clamp(40px, 5.4vw, 68px);
    line-height: 1.03;
    letter-spacing: -.034em;
    font-weight: 800;
    color: var(--sys-text-primary, #17191b);
}

.uhero h1 .uhero-line {
    display: block;
}

.uhero h1 em {
    font-style: normal;
    color: var(--sys-text-secondary, #73787b);
    margin-inline-start: 0;
}

/* A slow highlight travelling across the second line. Guarded, because the fallback for an
   unsupported background-clip is invisible text. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .uhero h1 em {
        background-image: linear-gradient(100deg,
                currentColor 0 34%,
                rgb(var(--u-ink) / .42) 47%,
                currentColor 60%);
        background-size: 260% 100%;
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
        color: var(--sys-text-secondary, #73787b);
        animation: u-sheen 11s linear infinite;
    }
}

.uhero-lede {
    margin: 0 0 24px;
    max-width: 58ch;
    font-size: clamp(16px, 1.45vw, 19px);
    line-height: 1.62;
    color: var(--sys-text-secondary, #73787b);
}

.uhero-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}

.uhero-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 12px;
    min-height: 52px;
    padding: 0 30px;
    border: 1px solid var(--sys-action-primary, #17191b);
    border-radius: 12px;
    background: var(--sys-action-primary, #17191b);
    color: var(--sys-action-primary-text, #ffffff) !important;
    font-size: 15.5px;
    font-weight: 700;
    text-decoration: none;
    overflow: hidden;
    box-shadow: 0 6px 20px rgb(var(--u-ink) / .20);
    transition: transform .2s var(--u-ease), box-shadow .2s ease, opacity .2s ease;
}

.uhero-btn,
.uhero-btn:visited,
.uhero-btn i,
.uhero-btn span {
    color: var(--sys-action-primary-text, #ffffff) !important;
}

/* A light pass across the button on hover — the same gesture as the hero sheen, once. */
.uhero-btn::after {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: -60%;
    width: 45%;
    background: linear-gradient(100deg, transparent, rgb(var(--u-paper) / .35), transparent);
    transform: skewX(-18deg);
    opacity: 0;
    pointer-events: none;
}

.uhero-btn:hover,
.uhero-btn:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 14px 34px rgb(var(--u-ink) / .28);
}

.uhero-btn:hover::after {
    animation: u-btn-sweep .75s var(--u-ease) forwards;
}

.uhero-btn:active {
    transform: translateY(0);
}

.uhero-btn[disabled] {
    opacity: .6;
    cursor: default;
    transform: none;
}

.uhero-btn.is-ghost {
    background: transparent;
    border-color: var(--sys-border, #e5e6e4);
    box-shadow: none;
}

.uhero-btn.is-ghost,
.uhero-btn.is-ghost:visited,
.uhero-btn.is-ghost i,
.uhero-btn.is-ghost span {
    color: var(--sys-text-primary, #17191b) !important;
}

.uhero-btn.is-ghost:hover {
    background: var(--sys-surface-hover, #eff0ee);
    border-color: var(--sys-text-primary, #17191b);
}

.uhero-btn:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 3px;
}

/* The four departments a request can reach. */
.uhero-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 9px;
    margin: 0 0 14px;
}

.upill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 15px;
    border-radius: 999px;
    background: var(--sys-surface-raised, #ffffff);
    border: 1px solid var(--sys-border, #e5e6e4);
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: .01em;
    color: var(--sys-text-primary, #17191b);
    box-shadow: 0 1px 3px rgb(var(--u-ink) / .04);
    cursor: default;
    transition: border-color .18s ease, transform .18s var(--u-ease), box-shadow .18s ease;
}

.upill:hover {
    border-color: var(--sys-text-primary, #17191b);
    transform: translateY(-2px);
    box-shadow: 0 8px 18px rgb(var(--u-ink) / .10);
}

.upill i {
    font-size: 11.5px;
    color: var(--sys-text-secondary, #73787b);
}

.uhero-note {
    margin: 0 0 4px;
    font-size: 12.5px;
    color: var(--sys-text-secondary, #73787b);
}

.uhero-note b {
    color: var(--sys-text-primary, #17191b);
}

.uhero-note button {
    border: 0;
    background: transparent;
    padding: 0;
    color: var(--sys-text-primary, #17191b);
    font: inherit;
    font-weight: 700;
    text-decoration: underline;
    cursor: pointer;
}

.uhero-note button:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 2px;
}

/* Three statements about how the portal works. Deliberately not metrics: this page makes no
   claim about speed or volume that the system is not showing you. */
.uhero-assure {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 28px;
    margin: 18px 0 0;
    padding: 16px 0 0;
    border-block-start: 1px solid var(--sys-border, #e5e6e4);
    list-style: none;
}

.uhero-assure li {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--sys-text-secondary, #73787b);
}

.uhero-assure i {
    font-size: 12px;
    opacity: .8;
}

/* The card stack. Every fill is a class, so the art follows the theme with the rest of the
   page instead of being patched by attribute selectors. */
.uhero-art {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
}

.uart {
    display: block;
    width: 100%;
    height: auto;
    max-height: min(64vh, 500px);
    color: var(--sys-text-primary, #17191b);
    overflow: visible;
}

.uart-stage {
    fill: rgb(var(--u-ink) / .02);
    stroke: rgb(var(--u-ink) / .10);
}

.uart-face {
    fill: var(--sys-surface-raised, #ffffff);
    stroke: rgb(var(--u-ink) / .12);
}

.uart-strong { fill: rgb(var(--u-ink) / .90); }
.uart-mid { fill: rgb(var(--u-ink) / .45); }
.uart-faint { fill: rgb(var(--u-ink) / .20); }
.uart-track { fill: rgb(var(--u-ink) / .10); }
.uart-fill { fill: rgb(var(--u-ink) / .85); }

.uart-plate {
    fill: rgb(var(--u-ink) / .06);
    stroke: rgb(var(--u-ink) / .12);
}

.uart-ok { fill: rgb(var(--u-ok) / .75); }
.uart-okdot { fill: rgb(var(--u-ok)); }

.uart-okwash {
    fill: rgb(var(--u-ok) / .12);
    stroke: rgb(var(--u-ok) / .32);
}

.uart-avatar {
    fill: rgb(var(--u-ink) / .14);
    stroke: var(--sys-surface-raised, #ffffff);
    stroke-width: 2;
}

.uart-plus { fill: rgb(var(--u-ink) / .90); }
.uart-plusmark { stroke: var(--sys-surface-raised, #ffffff); }

.uart-thread {
    stroke: rgb(var(--u-ink) / .28);
    stroke-width: 1.5;
    stroke-dasharray: 6 8;
    animation: u-thread 2.4s linear infinite;
}

.uart-card {
    filter: drop-shadow(0 6px 18px rgb(var(--u-ink) / .10));
    animation: u-float 7s ease-in-out infinite;
}

.uart-card-2 { animation-delay: -2.3s; }
.uart-card-3 { animation-delay: -4.6s; }

.uart-fill {
    transform-box: fill-box;
    transform-origin: left center;
    animation: u-progress 6.5s var(--u-ease) infinite;
}

.uart-okdot {
    transform-box: fill-box;
    transform-origin: center;
    animation: u-pulse 2.6s ease-in-out infinite;
}

.uart-sheen {
    animation: u-stage-sheen 9s var(--u-ease) infinite;
}

/* The invitation to keep reading. */
.uhero-cue {
    position: absolute;
    left: 50%;
    bottom: clamp(18px, 3.2vh, 32px);
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 9px 18px;
    border: 1px solid var(--sys-border, #e5e6e4);
    border-radius: 999px;
    background: var(--sys-surface-raised, #ffffff);
    color: var(--sys-text-secondary, #73787b);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    text-decoration: none;
    transform: translateX(-50%);
    box-shadow: 0 6px 20px rgb(var(--u-ink) / .08);
    transition: color .18s ease, border-color .18s ease,
        opacity .35s ease, transform .35s var(--u-ease);
}

/* Once the first band is on screen the cue has said what it had to say. */
.uhero-cue.is-gone {
    opacity: 0;
    transform: translateX(-50%) translateY(10px);
    pointer-events: none;
}

html[dir="rtl"] .uhero-cue {
    letter-spacing: normal;
    text-transform: none;
    font-size: 12.5px;
}

.uhero-cue:hover {
    color: var(--sys-text-primary, #17191b);
    border-color: var(--sys-text-primary, #17191b);
}

.uhero-cue i {
    animation: u-nudge 2.2s ease-in-out infinite;
}

.uhero-cue:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 3px;
}

/* ------------------------------------------------------------------ *
 * Bands — the sections below the fold
 * ------------------------------------------------------------------ */

.uband {
    position: relative;
    padding: var(--u-band) clamp(20px, 3.2vw, 72px);
}

/* No fill. The bands used to alternate paper against the page ground, which was a rhythm
   worth having until the backdrop became one fixed surface behind the whole page — a solid
   band is then a shutter closing over it. The rhythm is kept by the hairlines alone, so the
   sections are still separated and the texture runs the length of the page. */
.uband.is-inset {
    border-block: 1px solid var(--sys-border, #e5e6e4);
}

.uband-inner {
    width: 100%;
    max-width: var(--u-max);
    margin: 0 auto;
}

.uband-inner.is-narrow {
    max-width: 1100px;
}

.uband-head {
    max-width: 68ch;
    margin: 0 0 clamp(28px, 3.5vh, 48px);
}

.uband-label {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 12px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--sys-text-secondary, #73787b);
}

.uband-label::before {
    content: "";
    width: 22px;
    height: 2px;
    background: currentColor;
}

html[dir="rtl"] .uband-label {
    letter-spacing: normal;
    text-transform: none;
    font-size: 12.5px;
}

.uband-head h2 {
    margin: 0;
    font-size: clamp(26px, 3.3vw, 42px);
    line-height: 1.12;
    letter-spacing: -.03em;
    font-weight: 800;
    color: var(--sys-text-primary, #17191b);
}

.uband-lede {
    margin: 14px 0 0;
    max-width: 62ch;
    font-size: clamp(14.5px, 1.2vw, 17px);
    line-height: 1.6;
    color: var(--sys-text-secondary, #73787b);
}

/* A pointer-following highlight, set by home-motion.js as --mx/--my. Purely a hover
   affordance: it carries nothing and disappears with the pointer. */
.uspot {
    position: relative;
    isolation: isolate;
}

.uspot::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 0%),
            rgb(var(--u-ink) / .07), transparent 62%);
    opacity: 0;
    transition: opacity .3s ease;
    pointer-events: none;
}

.uspot:hover::after {
    opacity: 1;
}
/* ------------------------------------------------------------------ *
 * The three card families
 *
 * One layout, shared. Every card puts its icon, its title and its marker on the SAME first
 * row — icon at the reading edge, title beside it, marker at the far edge — and its sentence
 * on the row below, spanning the width. The step cards add a label row above all of it.
 *
 * The marker ("01", "IT") is a filled chip rather than the large faint number it was: a
 * watermark at 8% ink is decoration nobody can read, and these markers carry the order of
 * the process and the code of the department. Both are information, so both take the action
 * pair — ink plate, paper text — which inverts correctly in dark mode.
 * ------------------------------------------------------------------ */

.usteps {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 18px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ustep-card,
.uroute-card,
.ucap-card {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 14px;
    row-gap: 12px;
    padding: 24px 22px;
    border: 1px solid var(--sys-border, #e5e6e4);
    border-radius: 18px;
    background: var(--sys-surface-raised, #ffffff);
    box-shadow: 0 1px 2px rgb(var(--u-ink) / .03);
    overflow: hidden;
    transition: transform .28s var(--u-ease), border-color .28s ease, box-shadow .28s ease;
}

.ustep-card {
    grid-template-areas:
        "label label label"
        "icon  title marker"
        "text  text  text";
}

.uroute-card {
    grid-template-areas:
        "icon title marker"
        "text text text";
}

/* No marker on these, so no third track — an empty track still costs a column gap. */
.ucap-card {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
        "icon title"
        "text text";
}

.ustep-card b { grid-area: label; }
.ustep-card i,
.uroute-card i,
.ucap-card i { grid-area: icon; }
.ustep-card h3,
.uroute-card h3,
.ucap-card h3 { grid-area: title; }
.ustep-index,
.uroute-code { grid-area: marker; }
.ustep-card p,
.uroute-card p,
.ucap-card p { grid-area: text; }

/* Cards keep their surface — they are what the reader reads off, and one card treatment now
   serves every band, since no band has a fill of its own to sit against any more. */

.ustep-card:hover,
.uroute-card:hover,
.ucap-card:hover {
    transform: translateY(-4px);
    border-color: rgb(var(--u-ink) / .30);
    box-shadow: 0 18px 40px rgb(var(--u-ink) / .10);
}

/* A hairline that draws itself across the top of a card on hover. */
.ustep-card::before,
.uroute-card::before,
.ucap-card::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    height: 2px;
    background: linear-gradient(90deg, transparent, rgb(var(--u-ink) / .55), transparent);
    transform: scaleX(0);
    transition: transform .45s var(--u-ease);
}

.ustep-card:hover::before,
.uroute-card:hover::before,
.ucap-card:hover::before {
    transform: scaleX(1);
}

.ustep-card i,
.uroute-card i,
.ucap-card i {
    display: grid;
    place-items: center;
    width: 42px;
    height: 42px;
    margin: 0;
    border-radius: 13px;
    background: rgb(var(--u-ink) / .06);
    font-size: 17px;
    color: var(--sys-text-primary, #17191b);
    transition: background-color .28s ease, color .28s ease, transform .28s var(--u-ease);
}

.ustep-card:hover i,
.uroute-card:hover i,
.ucap-card:hover i {
    background: var(--sys-action-primary, #17191b);
    color: var(--sys-action-primary-text, #ffffff);
    transform: translateY(-2px);
}

/* The markers. Full contrast, because they are read. */
.ustep-index,
.uroute-code {
    justify-self: end;
    display: inline-grid;
    place-items: center;
    min-width: 34px;
    height: 26px;
    padding: 0 9px;
    border-radius: 8px;
    background: var(--sys-action-primary, #17191b);
    color: var(--sys-action-primary-text, #ffffff);
    font-size: 12px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    letter-spacing: .04em;
    line-height: 1;
    white-space: nowrap;
    transition: transform .28s var(--u-ease);
}

.ustep-card:hover .ustep-index,
.uroute-card:hover .uroute-code {
    transform: translateY(-2px) scale(1.06);
}

.ustep-card b {
    margin: 0;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--sys-text-primary, #17191b);
    opacity: .78;
}

html[dir="rtl"] .ustep-card b {
    letter-spacing: normal;
    text-transform: none;
    font-size: 11.5px;
}

.ustep-card h3,
.uroute-card h3,
.ucap-card h3 {
    margin: 0;
    font-size: 16.5px;
    font-weight: 700;
    letter-spacing: -.015em;
    line-height: 1.25;
    color: var(--sys-text-primary, #17191b);
}

.ustep-card p,
.uroute-card p,
.ucap-card p {
    margin: 0;
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--sys-text-secondary, #73787b);
}

/* ------------------------------------------------------------------ *
 * Where a request goes
 * ------------------------------------------------------------------ */

.uroutes {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 18px;
}

.uroute-note {
    margin: 22px 0 0;
    font-size: 13.5px;
    color: var(--sys-text-secondary, #73787b);
}

/* ------------------------------------------------------------------ *
 * Inside the portal
 * ------------------------------------------------------------------ */

.ucaps {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 18px;
}

/* ------------------------------------------------------------------ *
 * Common questions
 * ------------------------------------------------------------------ */

.ufaq {
    display: grid;
    gap: 12px;
}

.ufaq-item {
    border: 1px solid var(--sys-border, #e5e6e4);
    border-radius: 14px;
    background: var(--sys-surface-raised, #ffffff);
    transition: border-color .2s ease, background-color .2s ease;
}

.ufaq-item:hover {
    border-color: rgb(var(--u-ink) / .28);
}

.ufaq-item[open] {
    border-color: rgb(var(--u-ink) / .28);
}

.ufaq-item summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px 20px;
    cursor: pointer;
    font-size: 15.5px;
    font-weight: 700;
    color: var(--sys-text-primary, #17191b);
    list-style: none;
}

.ufaq-item summary::-webkit-details-marker {
    display: none;
}

.ufaq-item summary:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: -2px;
    border-radius: 13px;
}

.ufaq-item summary i {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: rgb(var(--u-ink) / .06);
    font-size: 11px;
    color: var(--sys-text-secondary, #73787b);
    transition: transform .3s var(--u-ease), background-color .2s ease;
}

.ufaq-item[open] summary i {
    transform: rotate(45deg);
    background: rgb(var(--u-ink) / .12);
}

.ufaq-body {
    padding: 0 20px 20px;
}

.ufaq-body p {
    margin: 0;
    max-width: 70ch;
    font-size: 14px;
    line-height: 1.68;
    color: var(--sys-text-secondary, #73787b);
}

.ufaq-item[open] .ufaq-body {
    animation: u-open .34s var(--u-ease) both;
}

/* ------------------------------------------------------------------ *
 * The closing invitation
 *
 * An ink plate — the one place on this page that inverts. Both of its colours come from the
 * action pair, so the plate and its text flip together in dark mode and the button on top of
 * it takes the opposite pair rather than a literal white.
 * ------------------------------------------------------------------ */

.ucta {
    padding: clamp(48px, 7vh, 88px) clamp(20px, 3.2vw, 72px);
}

/* The plate is sized by what is on it, not by the page. `fit-content` takes the widest of
   the heading, the 54ch sentence and the action row, so the ink never stretches into a band
   with a small sentence stranded in the middle of it. */
.ucta-inner {
    position: relative;
    width: fit-content;
    max-width: 100%;
    margin: 0 auto;
    padding: clamp(26px, 3vw, 40px) clamp(24px, 5vw, 72px);
    border-radius: 26px;
    background: var(--sys-action-primary, #17191b);
    color: var(--sys-action-primary-text, #ffffff);
    text-align: center;
    overflow: hidden;
    box-shadow: 0 24px 60px rgb(var(--u-ink) / .22);
}

/* A slow wash inside the plate, mixed from the plate's own text colour so it works in both
   directions of the inversion. */
.ucta-inner::before {
    content: "";
    position: absolute;
    inset: -40%;
    background:
        radial-gradient(closest-side circle at 26% 30%, currentColor, transparent),
        radial-gradient(closest-side circle at 78% 72%, currentColor, transparent);
    opacity: .10;
    animation: u-drift-c 30s var(--u-ease) infinite alternate;
    pointer-events: none;
}

.ucta-inner > * {
    position: relative;
}

.ucta-inner h2 {
    margin: 0 0 12px;
    font-size: clamp(26px, 3.6vw, 44px);
    line-height: 1.1;
    letter-spacing: -.03em;
    font-weight: 800;
    color: inherit;
}

.ucta-inner p {
    margin: 0 auto;
    max-width: 54ch;
    font-size: clamp(14.5px, 1.2vw, 17px);
    line-height: 1.6;
    color: inherit;
    opacity: .82;
}

.ucta-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
    margin-top: 28px;
}

.ucta-actions:empty {
    margin-top: 0;
}

.ucta-actions .uhero-btn,
.ucta-actions .uhero-btn:visited {
    background: var(--sys-action-primary-text, #ffffff);
    border-color: transparent;
    box-shadow: 0 12px 30px rgb(0 0 0 / .22);
}

.ucta-actions .uhero-btn,
.ucta-actions .uhero-btn:visited,
.ucta-actions .uhero-btn i,
.ucta-actions .uhero-btn span {
    color: var(--sys-action-primary, #17191b) !important;
}

.ucta-actions .uhero-btn:hover {
    opacity: .9;
}

.ucta-actions .uhero-btn::after {
    background: linear-gradient(100deg, transparent, rgb(var(--u-ink) / .25), transparent);
}

.ucta-actions .uhero-btn.is-ghost,
.ucta-actions .uhero-btn.is-ghost:visited {
    background: transparent;
    border-color: currentColor;
    box-shadow: none;
}

.ucta-actions .uhero-btn.is-ghost,
.ucta-actions .uhero-btn.is-ghost:visited,
.ucta-actions .uhero-btn.is-ghost i,
.ucta-actions .uhero-btn.is-ghost span {
    color: var(--sys-action-primary-text, #ffffff) !important;
}

.ucta-actions .uhero-btn.is-ghost:hover {
    background: rgb(var(--u-paper) / .12);
    border-color: currentColor;
}

.ucta-actions .uhero-btn:focus-visible {
    outline: 2px solid var(--sys-action-primary-text, #ffffff);
    outline-offset: 3px;
}

/* ------------------------------------------------------------------ *
 * The closing plate's own furniture
 *
 * The plate is the last thing on the page now that the foot is gone, so it carries the two
 * lines the foot used to: how signing in works, and the copyright. Both sit under the action
 * at a lower contrast — present for whoever looks, silent for whoever does not.
 * ------------------------------------------------------------------ */

.ucta-eyebrow {
    margin: 0 0 14px;
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: inherit;
    opacity: .55;
}

html[dir="rtl"] .ucta-eyebrow {
    letter-spacing: normal;
    text-transform: none;
    font-size: 12px;
}

.ucta-foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin: 26px 0 0 !important;
    max-width: none !important;
    font-size: 11.5px;
    color: inherit;
    opacity: .55 !important;
}

/* ------------------------------------------------------------------ *
 * The way through to every other question
 * ------------------------------------------------------------------ */

.ufaq-more {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: 20px;
    padding: 13px 22px;
    border: 1px solid var(--sys-border, #e5e6e4);
    border-radius: 999px;
    background: var(--sys-surface-raised, #ffffff);
    color: var(--sys-text-primary, #17191b);
    font-size: 13.5px;
    font-weight: 700;
    text-decoration: none;
    transition: border-color .2s ease, transform .2s var(--u-ease), box-shadow .2s ease;
}

.ufaq-more:hover {
    border-color: var(--sys-text-primary, #17191b);
    transform: translateY(-2px);
    box-shadow: 0 10px 24px rgb(var(--u-ink) / .10);
}

.ufaq-more i {
    font-size: 11px;
    transition: transform .25s var(--u-ease);
}

html[dir="rtl"] .ufaq-more i {
    transform: scaleX(-1);
}

.ufaq-more:hover i {
    transform: translateX(3px);
}

html[dir="rtl"] .ufaq-more:hover i {
    transform: scaleX(-1) translateX(3px);
}

.ufaq-more:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 3px;
}

/* ================================================================== *
 * The questions page — /user/faq
 *
 * Everything the portal is asked about itself, on one page: a heading that says what the page
 * is, a filter over what is already rendered, a category rail that sticks beside the answers,
 * and the answers themselves in the same .ufaq-item component the front door uses. Sharing
 * that component is the point — an answer should not look like a different kind of thing
 * depending on which page it is read on.
 *
 * The rail is navigation, not decoration: it holds a count per section, hides a section that
 * the filter has emptied, and is generated from the same list as the answers.
 * ================================================================== */

.ufaqpage {
    --u-ink: 23 25 27;
    --u-paper: 255 255 255;
    --u-ok: 19 129 59;
    --u-bar: 66px;
    --u-ease: cubic-bezier(.16, 1, .3, 1);

    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 0 0 clamp(40px, 6vh, 80px);
}

:root.dark-theme .ufaqpage,
:root[data-theme="dark"] .ufaqpage {
    --u-ink: 233 234 232;
    --u-paper: 24 26 28;
    --u-ok: 111 207 149;
}

/* ------------------------------------------------------------------ *
 * Head
 * ------------------------------------------------------------------ */

.ufaq-head {
    padding: clamp(32px, 6vh, 72px) clamp(20px, 3.2vw, 72px) clamp(28px, 4vh, 48px);
}

/* Full width, with the page gutter as its only margin. What keeps this readable is that the
   MEASURE moves inward rather than the container: the heading and the lede below carry their
   own ch limits, so the section fills the screen while the prose stays the width an eye can
   track. Stretching a paragraph to 2000px is not a wider page, it is an unread one. */
.ufaq-head-inner {
    max-width: none;
    margin: 0;
}

.ufaq-head h1 {
    max-width: 22ch;
}

.ufaq-back {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 26px;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--sys-text-secondary, #73787b);
    text-decoration: none;
    transition: color .16s ease;
}

.ufaq-back:hover {
    color: var(--sys-text-primary, #17191b);
}

.ufaq-back i {
    font-size: 11px;
}

html[dir="rtl"] .ufaq-back i {
    transform: scaleX(-1);
}

.ufaq-back:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 3px;
    border-radius: 4px;
}

.ufaq-head h1 {
    margin: 0;
    font-size: clamp(32px, 4.4vw, 54px);
    line-height: 1.06;
    letter-spacing: -.032em;
    font-weight: 800;
    color: var(--sys-text-primary, #17191b);
}

.ufaq-lede {
    margin: 18px 0 0;
    max-width: 74ch;
    font-size: clamp(15.5px, 1.25vw, 18px);
    line-height: 1.7;
    color: var(--sys-text-primary, #17191b);
    opacity: .82;
}

/* The filter. Wide, because it is the fastest route to an answer on a page this long — but
   capped, because past about a thousand pixels a text field stops reading as a field. */
.ufaq-search {
    max-width: 1040px;
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 28px;
    padding: 0 16px;
    border: 1px solid var(--sys-border, #e5e6e4);
    border-radius: 14px;
    background: var(--sys-surface-raised, #ffffff);
    transition: border-color .18s ease, box-shadow .18s ease;
}

.ufaq-search:focus-within {
    border-color: var(--sys-text-primary, #17191b);
    box-shadow: var(--sys-focus-ring, 0 0 0 4px rgba(23, 25, 27, .10));
}

.ufaq-search > i {
    flex: 0 0 auto;
    font-size: 13px;
    color: var(--sys-text-secondary, #73787b);
}

.ufaq-search input {
    flex: 1 1 auto;
    min-width: 0;
    height: 54px;
    border: 0;
    background: transparent;
    color: var(--sys-text-primary, #17191b);
    font: inherit;
    font-size: 15px;
}

.ufaq-search input:focus {
    outline: none;
}

.ufaq-search input::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

.ufaq-search button {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: 50%;
    background: rgb(var(--u-ink) / .07);
    color: var(--sys-text-secondary, #73787b);
    font-size: 11px;
    cursor: pointer;
}

.ufaq-search button:hover {
    background: rgb(var(--u-ink) / .13);
    color: var(--sys-text-primary, #17191b);
}

.ufaq-search button:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 2px;
}

/* Visually hidden, still read aloud: the field needs a label, the design does not. */
.ufaq-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.ufaq-count {
    margin: 12px 0 0;
    min-height: 18px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--sys-text-secondary, #73787b);
}

/* ------------------------------------------------------------------ *
 * Body — rail beside the answers
 * ------------------------------------------------------------------ */

.ufaq-body-grid {
    display: grid;
    grid-template-columns: minmax(0, 270px) minmax(0, 1fr);
    align-items: start;
    gap: clamp(24px, 3.5vw, 72px);
    max-width: none;
    margin: 0;
    padding: 0 clamp(20px, 3.2vw, 72px);
}

.ufaq-rail {
    position: sticky;
    /* Clears the floating bar, which is 66px and sticky itself. */
    top: calc(var(--u-bar) + 20px);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.ufaq-rail-head {
    margin: 0 0 12px;
    padding-inline-start: 14px;
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--sys-text-secondary, #73787b);
}

html[dir="rtl"] .ufaq-rail-head {
    letter-spacing: normal;
    text-transform: none;
    font-size: 12px;
}

.ufaq-rail a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
    border-radius: 10px;
    color: var(--sys-text-secondary, #73787b);
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.35;
    text-decoration: none;
    transition: background-color .16s ease, color .16s ease;
}

.ufaq-rail a:hover {
    background: rgb(var(--u-ink) / .05);
    color: var(--sys-text-primary, #17191b);
}

.ufaq-rail a:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: -2px;
}

.ufaq-rail a b {
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--sys-text-secondary, #73787b);
    opacity: .7;
}

.ufaq-groups {
    display: grid;
    gap: clamp(36px, 5vh, 64px);
    min-width: 0;
}

/* The reason the page can be full width and still read well. Each answer is a card at a fixed
   comfortable width and the grid fits as many as the screen allows — one column on a laptop,
   two or three on a wide monitor. The alternative, one column stretched across the viewport,
   is the thing that makes full-width pages unreadable.

   align-items: start so an opened answer grows on its own instead of stretching every card
   beside it to match. */
.ufaqpage .ufaq {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
    align-items: start;
    gap: 14px;
}

@media (max-width: 560px) {
    .ufaqpage .ufaq {
        grid-template-columns: 1fr;
    }
}

.ufaq-group {
    scroll-margin-top: calc(var(--u-bar) + 24px);
}

.ufaq-group-head {
    display: flex;
    align-items: baseline;
    gap: 14px;
    margin: 0 0 20px;
    padding-bottom: 14px;
    border-bottom: 2px solid var(--sys-text-primary, #17191b);
    font-size: clamp(20px, 1.7vw, 26px);
    font-weight: 800;
    letter-spacing: -.025em;
    color: var(--sys-text-primary, #17191b);
}

.ufaq-group-head b {
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--sys-text-secondary, #73787b);
    opacity: .7;
}

/* The items are the front door's .ufaq-item; what changes here is contrast and scale, because
   this page is read rather than skimmed. The question and the answer are both primary ink —
   hierarchy comes from weight and size, not from fading the answer to grey. Setting the one
   page whose whole purpose is to be read in the palest tone the palette owns was the single
   biggest thing standing between it and legibility. */
.ufaqpage .ufaq-item {
    scroll-margin-top: calc(var(--u-bar) + 24px);
    border-radius: 16px;
    transition: border-color .2s ease, box-shadow .2s ease, background-color .2s ease;
}

.ufaqpage .ufaq-item:hover {
    border-color: rgb(var(--u-ink) / .34);
    box-shadow: 0 8px 24px rgb(var(--u-ink) / .07);
}

/* An open answer is the one being read: it says so with a solid edge rather than a tint. */
.ufaqpage .ufaq-item[open] {
    border-color: var(--sys-text-primary, #17191b);
    box-shadow: 0 10px 30px rgb(var(--u-ink) / .09);
}

.ufaqpage .ufaq-item summary {
    padding: 20px 22px;
    font-size: 16px;
    font-weight: 700;
    line-height: 1.4;
    color: var(--sys-text-primary, #17191b);
}

.ufaqpage .ufaq-item[open] summary {
    padding-bottom: 14px;
}

.ufaqpage .ufaq-item summary i {
    width: 30px;
    height: 30px;
    font-size: 12px;
}

.ufaqpage .ufaq-item[open] summary i {
    background: var(--sys-action-primary, #17191b);
    color: var(--sys-action-primary-text, #ffffff);
}

.ufaqpage .ufaq-body {
    padding: 0 22px 22px;
}

.ufaqpage .ufaq-body p {
    max-width: 68ch;
    font-size: 15px;
    line-height: 1.78;
    color: var(--sys-text-primary, #17191b);
    opacity: .88;
}

.ufaq-permalink {
    display: inline-block;
    margin-top: 12px;
    font-size: 12px;
    font-weight: 700;
    color: var(--sys-text-secondary, #73787b);
    text-decoration: none;
    opacity: 0;
    transition: opacity .18s ease, color .18s ease;
}

.ufaq-item[open]:hover .ufaq-permalink,
.ufaq-permalink:focus-visible {
    opacity: 1;
}

.ufaq-permalink:hover {
    color: var(--sys-text-primary, #17191b);
    text-decoration: underline;
}

.ufaq-permalink:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 3px;
    border-radius: 3px;
}

.ufaq-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    max-width: 620px;
    margin: clamp(32px, 6vh, 64px) auto 0;
    padding: 0 24px;
    font-size: 14.5px;
    line-height: 1.6;
    text-align: center;
    color: var(--sys-text-secondary, #73787b);
}

.ufaq-empty i {
    flex: 0 0 auto;
    opacity: .6;
}

.ufaqpage .ucta {
    margin-top: clamp(48px, 8vh, 96px);
}

/* The plate closes a page that has already said everything, so it is sized to its three lines
   rather than to the ceremony the front door's plate earns. Width was already fit-content;
   this takes the height in too — the vertical padding was set for a plate that ends a scroll,
   and here it left a band of empty ink above and below the words. */
.ufaqpage .ucta-inner {
    padding: clamp(30px, 3.4vw, 44px) clamp(28px, 4vw, 56px);
    border-radius: 20px;
}

.ufaqpage .ucta-inner h2 {
    margin-bottom: 8px;
    font-size: clamp(22px, 2.4vw, 30px);
}

.ufaqpage .ucta-inner p {
    font-size: clamp(14px, 1.05vw, 15.5px);
}

.ufaqpage .ucta-actions {
    margin-top: 20px;
}

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

    /* The rail stops being a rail: a horizontal strip above the answers, scrolled sideways
       inside itself so the page never does. */
    .ufaq-rail {
        position: static;
        flex-direction: row;
        flex-wrap: nowrap;
        gap: 8px;
        overflow-x: auto;
        padding-bottom: 6px;
        margin-bottom: 8px;
        scrollbar-width: thin;
    }

    .ufaq-rail-head {
        display: none;
    }

    .ufaq-rail a {
        flex: 0 0 auto;
        border: 1px solid var(--sys-border, #e5e6e4);
        border-radius: 999px;
        white-space: nowrap;
    }
}


/* ------------------------------------------------------------------ *
 * Reveal on scroll
 *
 * home-motion.js adds .u-motion to <html> before anything is armed, so a browser without
 * JavaScript — or one that failed to load the module — shows the whole page at full opacity.
 * ------------------------------------------------------------------ */

:root.u-motion [data-reveal] {
    opacity: 0;
    transform: translateY(20px);
    transition:
        opacity .75s var(--ecv-ease, cubic-bezier(.16, 1, .3, 1)),
        transform .75s var(--ecv-ease, cubic-bezier(.16, 1, .3, 1));
    transition-delay: var(--d, 0ms);
    will-change: opacity, transform;
}

:root.u-motion [data-reveal].is-in {
    opacity: 1;
    transform: none;
}

/* ------------------------------------------------------------------ *
 * Keyframes
 * ------------------------------------------------------------------ */

@keyframes u-rise {
    from { opacity: 0; transform: translateY(22px); }
    to { opacity: 1; transform: none; }
}

@keyframes u-sheen {
    from { background-position: 180% 0; }
    to { background-position: -80% 0; }
}

@keyframes u-btn-sweep {
    from { inset-inline-start: -60%; opacity: 1; }
    to { inset-inline-start: 130%; opacity: 0; }
}

@keyframes u-drift-c {
    from { transform: translate3d(0, 0, 0) scale(.95); }
    to { transform: translate3d(-4vw, 4vh, 0) scale(1.15); }
}

@keyframes u-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-8px); }
}

@keyframes u-progress {
    0% { transform: scaleX(.08); }
    55% { transform: scaleX(.62); }
    100% { transform: scaleX(.62); }
}

@keyframes u-pulse {
    0%, 100% { transform: scale(1); opacity: 1; }
    50% { transform: scale(1.55); opacity: .55; }
}

@keyframes u-thread {
    to { stroke-dashoffset: -28; }
}

@keyframes u-stage-sheen {
    0% { transform: translateX(0); }
    55%, 100% { transform: translateX(820px); }
}

@keyframes u-nudge {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(3px); }
}

@keyframes u-open {
    from { opacity: 0; transform: translateY(-6px); }
    to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ *
 * Narrower, shorter, and quieter
 * ------------------------------------------------------------------ */

@media (max-width: 1180px) {
    .usteps,
    .uroutes {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

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

}

@media (max-width: 1000px) {
    .uhero {
        min-height: 0;
        padding-block: clamp(28px, 5vh, 56px) clamp(72px, 8vh, 96px);
    }

    .uhero-inner {
        grid-template-columns: 1fr;
        gap: 32px;
    }

    .uhero-copy {
        max-width: none;
    }

    .uhero-art {
        order: 2;
        width: 100%;
        max-width: 520px;
        margin-inline: auto;
    }

    .uart {
        max-height: none;
    }
}

@media (max-width: 640px) {
    .usteps,
    .uroutes,
    .ucaps {
        grid-template-columns: 1fr;
    }

    .uhero-assure {
        flex-direction: column;
        gap: 10px;
    }

    .ucta-inner {
        border-radius: 20px;
    }
}

/* A short window — a laptop in a browser with a large bookmarks bar — should not be forced
   to scroll past an empty hero. */
@media (max-height: 700px) and (min-width: 1000px) {
    .uhero {
        min-height: 0;
        padding-block: 40px 72px;
    }
}

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

    .uhero-copy > *,
    .uhero-art,
    .uhero h1 em,
    .uart-card,
    .uart-fill,
    .uart-okdot,
    .uart-sheen,
    .uart-thread,
    .uhero-cue i,
    .ucta-inner::before,
    .ufaq-item[open] .ufaq-body,
    .uhero-btn:hover::after {
        animation: none !important;
    }

    :root.u-motion [data-reveal] {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .ustep-card,
    .uroute-card,
    .ucap-card,
    .upill,
    .uhero-btn {
        transition: none;
    }

    .ustep-card:hover,
    .uroute-card:hover,
    .ucap-card:hover,
    .upill:hover,
    .uhero-btn:hover {
        transform: none;
    }
}

/* ================================================================== *
 * Sign-in
 *
 * A single card on the open canvas, under the same bar as every other screen — so the
 * sign-in page is visibly part of this portal and not a detour out of it. There is no Back
 * control: this page is the portal's entrance, and there is frequently nothing behind it.
 * ================================================================== */

.ulogin {
    min-height: 100svh;
    display: grid;
    grid-template-rows: auto 1fr;
}

.ulogin-body {
    display: grid;
    place-items: center;
    padding: clamp(28px, 7vw, 84px) 20px 90px;
}

.ulogin-card {
    width: 100%;
    max-width: 448px;
    background: var(--ecv-paper, #fff);
    border: 1px solid var(--ecv-line, #e5e6e4);
    border-radius: 18px;
    padding: clamp(26px, 4vw, 40px);
    display: grid;
    gap: 18px;
}

.ulogin-card h1 {
    margin: 0;
    font-size: clamp(23px, 3.2vw, 30px);
    letter-spacing: -.02em;
    line-height: 1.2;
}

.ulogin-card > p:first-of-type {
    margin: -8px 0 0;
    color: var(--ecv-muted, #73787b);
    font-size: 14.5px;
    line-height: 1.65;
}

.ulogin-mark {
    height: 30px;
    width: auto;
    margin-bottom: 4px;
}

/* ================================================================== *
 * Unauthorized
 * ================================================================== */

.up-portal-user .denied-card {
    border-radius: 18px;
}

/* ================================================================== *
 * Authenticated screens
 *
 * The working pages (dashboard, requests, notifications, articles, announcements,
 * feedback, profile) were built around a fixed sidebar. Their content is unchanged — the
 * cards, tables, pills and modals are all still the .up-* components — but the frame is now
 * this portal's top bar, so the shell collapses from two columns to one.
 *
 * Overriding here rather than editing user_portal.css keeps the change where the portal's
 * identity lives: the content system is shared with administration's screens, the chrome is
 * not, and only the chrome differs.
 * ================================================================== */

.up-portal-user .up-shell {
    display: block;
    min-height: 0;
}

.up-portal-user .up-main {
    max-width: 100%;
    width: 100%;
    margin: 0;
    padding: clamp(20px, 3vw, 34px) clamp(16px, 3vw, 40px) 96px;
    box-sizing: border-box;
}

.up-portal-user .up-main > * {
    max-width: 100%;
    min-width: 0;
}

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

.up-portal-user .up-page-head h1 {
    margin: 0;
}

.up-portal-user .up-page-head p {
    margin: 0;
}

/* The sidebar, its floating action button and the fixed language corner all belonged to the
 * old frame. Their markup is no longer emitted; these rules make certain that a stale
 * cached stylesheet or a screen that has not been rebuilt cannot resurrect them. */
.up-portal-user .up-sidebar,
.up-portal-user .up-fab,
.up-portal-user .up-langbar {
    display: none !important;
}

/* ================================================================== *
 * Persistent side navigation
 * ================================================================== */
.up-portal-user .pnav-account-zone {
    width: 100%;
    padding: 12px 0 0;
    border-top-color: var(--ecv-line, #e5e6e4);
}
.up-portal-user .pnav-user-card {
    background: linear-gradient(145deg, var(--ecv-ink, #17191b), #303438);
    border-color: rgba(255,255,255,.1);
    text-decoration: none;
}
.up-portal-user .pnav-signout {
    background: var(--ecv-soft, #eff0ee);
    border-color: var(--ecv-line, #e5e6e4);
    color: var(--ecv-ink, #17191b);
}
.up-portal-user .pnav-signout:hover { color: var(--ecv-red, #ad291f); background: #fff3f1; }

@media (max-width: 920px) {
    .up-portal-user .pnav-account-zone { padding-top: 8px; }
}

/* Latest approved reference: compact, flat and restrained. */
.up-portal-user .pnav-account-zone { padding: 12px 3px 0; }

/* ================================================================== *
 * Reference-matched sidebar
 * ================================================================== */
.up-portal-user .pnav-account-zone { padding: 13px 3px 0; border-top-color: rgba(255,255,255,.12); }
.up-portal-user .pnav-signout { background: transparent; border: 0; color: rgba(255,255,255,.72); }
.up-portal-user .pnav-signout:hover { background: rgba(255,255,255,.08); color: #fff; }
.up-portal-user .pnav-account-zone { width: 100%; padding: 10px 0 0; }

/* The rail tokens live in shared/css/portal-chrome.css — one treatment for all three
 * portals, themed by ecvalla-theme.css. Nothing rail-coloured belongs in this file. */


/* ------------------------------------------------------------------ *
 * My Tickets — the table
 *
 * Rows are the way into a ticket, so they behave like controls: pointer, hover surface,
 * a visible keyboard focus, and the reference set tabular so a column of them aligns.
 * ------------------------------------------------------------------ */

.up-tickets-card {
    padding: 0;
    overflow: hidden;
}

.up-tickets-card .dept-status {
    padding: 16px 18px;
    margin: 0;
}

.up-ticket-row {
    cursor: pointer;
    transition: background-color .14s ease;
}

.up-ticket-row:hover {
    background: var(--sys-surface-hover, var(--ecv-soft, #eff0ee));
}

.up-ticket-row:focus-visible {
    outline: 2px solid var(--sys-text-primary, var(--ecv-ink, #17191b));
    outline-offset: -2px;
}

.up-ticket-ref {
    font-variant-numeric: tabular-nums;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
    font-weight: 700;
    white-space: nowrap;
}

.up-ticket-subject {
    display: block;
    max-width: 46ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 700;
    color: var(--sys-text-primary, var(--text-color, #17191b));
}

/* ================================================================== *
 * Hero — filling the viewport, not floating in it
 *
 * The hero already claimed the full height, but its content was a ~440px block centred in a
 * ~1010px space: correct by the letter, wrong to look at. Two dead bands, one above and one
 * below, and a composition that read as small.
 *
 * What a large corporate hero actually does is DISTRIBUTE. The content becomes three bands
 * down the column — the label, the statement, the reassurance — pushed apart with
 * space-between so they meet the padding rather than huddling at the centre. The type scale
 * then gains a viewport-height term, so a tall screen gets a bigger headline instead of more
 * emptiness.
 * ================================================================== */

.uhero {
    /* The band the content is allowed to occupy. Generous, but it is padding, not void. */
    /* Tight and deliberate. The bottom band is the only generous one, because the scroll
     * cue lives in it. */
    padding-block: clamp(16px, 2.2vh, 32px) clamp(58px, 6.6vh, 80px);
    align-items: stretch;
}

.uhero-inner {
    width: 100%;
    align-items: stretch;
}

/* The copy column becomes the distributor. `space-between` with the statement group allowed
 * to grow is what turns a centred lump into a composition that touches both edges. */
.uhero-copy {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: clamp(14px, 2vh, 28px);
    min-height: 100%;
}

/* Band 2 — the statement. It carries the growth, so the headline and its lede sit optically
 * centred while the label and the reassurance hold the top and bottom. */
.uhero-copy > h1 {
    margin-block: 0;
    /* The vh term is the point: on a 1080 screen this is ~76px, on a laptop ~56px, and the
     * hero fills either without the copy needing a different layout. */
    font-size: clamp(44px, 3.7vw + 1.9vh, 96px);
    line-height: 1.02;
    letter-spacing: -.032em;
}

.uhero-lede {
    margin-block: clamp(12px, 1.6vh, 22px) 0;
    max-width: 44ch;
    font-size: clamp(16px, .62vw + .62vh, 23px);
    line-height: 1.62;
}

.uhero-eyebrow { margin-block: 0; }

.uhero-actions {
    margin-block: clamp(14px, 2vh, 26px) 0;
}

.uhero-btn {
    min-height: clamp(52px, 6vh, 68px);
    padding-inline: clamp(26px, 2.3vw, 40px);
    font-size: clamp(15px, .4vw + .4vh, 18px);
}

/* Band 3 — the reassurance strip, held at the bottom of the column. */
.uhero-assure {
    margin-block: 0;
    padding-top: clamp(10px, 1.6vh, 20px);
}

.uhero-note { margin-block: clamp(10px, 1.4vh, 16px) 0; }

/* The art scales with the column instead of asserting a fixed height, so it stays in
 * proportion to a headline that now grows. */
.uhero-art {
    align-self: center;
    max-height: min(80vh, 760px);
}

.uhero-art svg,
.uhero-art img { max-height: min(80vh, 760px); height: auto; width: 100%; }

/* The scroll cue belongs to the hero's bottom edge, inside its padding — not adrift in the
 * space the distribution has just removed. Only the offset changes: the cue keeps the
 * left: 50% / translateX(-50%) centring and the inline-flex sizing that make it a pill rather
 * than a full-width bar. */
.uhero-cue {
    bottom: clamp(10px, 1.6vh, 20px);
}

/* Short viewports: stop distributing and let the hero grow past the fold rather than
 * compressing the statement into itself. */
@media (max-height: 620px) {
    .uhero {
        min-height: 0;
        padding-block: 40px 56px;
    }

    .uhero-copy { justify-content: flex-start; min-height: 0; }
    .uhero-cue { position: static; margin-top: 24px; align-self: center; transform: none; }
}

/* One column below the split point: the art follows the statement rather than competing
 * with it for a width neither can use. */
@media (max-width: 900px) {
    .uhero-copy { min-height: 0; justify-content: flex-start; }
    .uhero-art { max-height: 42vh; }
    .uhero-art svg, .uhero-art img { max-height: 42vh; }
}

/* ================================================================== *
 * Hero — rhythm and optical alignment
 *
 * Two corrections that are what separates "filled" from "composed".
 *
 * 1. RHYTHM. `space-between` distributed the leftover height into every gap at once, giving
 *    68 / 86 / 90 / 68 / 97 / 68px — six different spaces, none of them chosen. Related
 *    things must sit close together and the slack must pool in ONE deliberate place. The
 *    gaps below are a tight scale, and `margin-block-start: auto` on the trust strip absorbs
 *    everything left over, pinning it to the bottom edge. One intentional space instead of
 *    six accidental ones.
 *
 * 2. OPTICAL ALIGNMENT. Every box in the column starts at exactly the same x — measured, not
 *    assumed. What the eye sees is still a headline sitting a few pixels right of the text
 *    beneath it, because a glyph's ink starts inside its box (the side bearing), and at 91px
 *    that inset is ~4px while the body text's is under 1px. Display type has to be pulled
 *    left to line up with the column it heads. This is the difference between aligning boxes
 *    and aligning what people actually look at.
 * ================================================================== */

.uhero-copy {
    justify-content: flex-start;
    gap: 0;
}

/* The tight scale. Related bands sit close; nothing here competes with the pooled slack. */
.uhero-eyebrow { margin-block: auto clamp(18px, 2.4vh, 30px); }
.uhero-copy > h1 { margin-block: 0; }
.uhero-lede { margin-block: clamp(18px, 2.4vh, 28px) 0; }
.uhero-pills { margin-block: clamp(16px, 2vh, 24px) 0; }

/* The trust strip is pushed to the bottom edge, taking all the slack with it. The rule sits on
 * the SLOT, not on the note: the note is one of two states that appear only once the session is
 * known, and a margin that arrives with its element is a shift. */
.uhero-copy > .phero-slot-note {
    margin-block: auto 0;
    padding-top: clamp(16px, 2.2vh, 26px);
}

.uhero-assure {
    margin-block: clamp(12px, 1.6vh, 18px) 0;
    padding-top: clamp(12px, 1.6vh, 20px);
}

/* No optical compensation on the headline. Canvas metrics for this face put its left side
 * bearing at 1px (0.011em) at 91px — the same 1px as the lede at 18px — so the boxes and the
 * ink already line up, and a nudge would only break an alignment that is correct. */

/* ================================================================== *
 * Hero headline — room for the descenders
 *
 * The second line is painted through `background-clip: text`, which clips the gradient to the
 * glyphs but only paints inside the element's BACKGROUND box. With a line-height of 1.02 that
 * box ends at the line box, while Manrope's descenders reach about 0.17em below it — so the
 * tails of "g", "y" and "p" fell outside the paintable area and simply were not drawn. It read
 * as the text being cut off, and it was: measured 16px of ink below a 185px box.
 *
 * The padding extends the background box far enough down to cover them; the equal negative
 * margin gives that space straight back, so the line spacing and everything below it are
 * unchanged. The tight line-height is deliberate and is kept.
 * ================================================================== */

.uhero h1 em {
    padding-block-end: .2em;
    margin-block-end: -.2em;
}


/* ================================================================== *
 * Notifications
 * ================================================================== */

/* The heading and its one action share a row.
 *
 * .up-portal-user .up-page-head above forces a column with flex-start — the right shape for
 * a page head that is only a title and a lede, and the wrong one here, where the head also
 * carries "Mark all as read". Being later in the cascade and more specific, it beat
 * .up-notification-head's own row layout in user_portal.css and stacked the button under the
 * title. Stated here, at the specificity that wins, rather than by loosening the rule above:
 * every other page still wants the column. */
.up-portal-user .up-page-head.up-notification-head {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
}

.up-portal-user .up-notification-head .up-mark-read {
    flex: 0 0 auto;
    margin-inline-start: auto;
}

/* Below this the two no longer fit side by side, and a wrapped button at the end of a row is
   worse than a full-width one under it. */
@media (max-width: 640px) {
    .up-portal-user .up-page-head.up-notification-head {
        flex-direction: column;
        align-items: stretch;
        gap: 14px;
    }

    .up-portal-user .up-notification-head .up-mark-read {
        margin-inline-start: 0;
        justify-content: center;
    }
}

/* The filter bar is sized by its chips, not by the page.
 *
 * It is a two-chip segmented control inside a bordered plate. Stretched to the full width of
 * a monitor it read as an empty toolbar with two buttons stranded at one end — the border was
 * describing a region that had nothing in it. Sized to its content and centred, it reads as
 * the control it is. */
.up-portal-user .up-notification-toolbar {
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
    justify-content: center;
}

.up-portal-user .up-notification-toolbar .up-chiprow {
    justify-content: center;
    flex-wrap: nowrap;
}

/* ------------------------------------------------------------------ *
 * A notification says what happened, to what, by whom and when
 * ------------------------------------------------------------------ */

.up-portal-user .up-notification-facts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 14px;
    margin-top: 7px;
    font-size: 11.5px;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
}

.up-portal-user .up-notification-facts i {
    margin-inline-end: 5px;
    font-size: 10px;
    opacity: .75;
}

/* The reference is the thing a person quotes, so it is set to be read back: tabular, boxed,
   and never wrapped mid-token. */
.up-portal-user .up-notification-ref {
    padding: 2px 9px;
    border: 1px solid var(--sys-border, var(--border-color, #e5e6e4));
    border-radius: 999px;
    background: var(--sys-surface-page, var(--ecv-canvas, #f5f5f3));
    color: var(--sys-text-primary, var(--text-color, #17191b));
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.up-portal-user .up-notification-by,
.up-portal-user .up-notification-when {
    display: inline-flex;
    align-items: center;
    min-width: 0;
}

.up-portal-user .up-notification-when {
    font-variant-numeric: tabular-nums;
}

/* "Open ticket →" is an invitation, not a fact, so it sits apart from the facts row and only
   colours up when the row is under the pointer. */
.up-portal-user .up-notification-open {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: 8px;
    font-size: 12px;
    font-weight: 700;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
    transition: color .16s ease, gap .16s ease;
}

.up-portal-user .up-notification-row:hover .up-notification-open {
    color: var(--sys-text-primary, var(--text-color, #17191b));
    gap: 10px;
}

html[dir="rtl"] .up-portal-user .up-notification-open i {
    transform: scaleX(-1);
}

/* An unread row is stated twice — a dot for the eye and weight for the reading — because a
   dot alone is easy to miss in a long list. */
.up-portal-user .up-notification-row.is-unread .up-row-body > b {
    font-weight: 800;
}

.up-portal-user .up-notification-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 0 0 auto;
    white-space: nowrap;
}


/* ================================================================== *
 * Rating stars
 *
 * Five stars, each one two targets. A star is drawn twice — an outline and a filled copy
 * clipped over it — so a half is a real half of the same shape rather than a second glyph
 * that approximates one. The two buttons are transparent hit areas over each half.
 * ================================================================== */

.up-portal-user .up-stars {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    /* The stars are the control on this page; at 22px they were an afterthought beside the
       comment box. */
    font-size: clamp(30px, 3.4vw, 40px);
    line-height: 1;
}

.up-portal-user .up-star {
    position: relative;
    display: inline-block;
    width: 1em;
    height: 1em;
    color: var(--sys-border, var(--border-color, #e5e6e4));
}

.up-portal-user .up-star i {
    position: absolute;
    inset: 0;
    display: block;
    line-height: 1;
    pointer-events: none;
}

.up-portal-user .up-star-base {
    color: inherit;
}

/* The filled copy, revealed left-to-right. width is what makes a half a half. */
/* Each star takes its own share of ONE number.
 *
 * --rate is the value being shown, 0 to 5, set on the group by bindStarPreview: the resting
 * score normally, whatever is under the pointer while hovering. --i is this star's position.
 * The clamp turns that pair into this star's fill: full below the rate, empty above it, and a
 * fraction for the one the rate lands inside — which is exactly where a half comes from.
 *
 * Because all five read the same number, moving the pointer across the row animates the whole
 * row at once instead of five separate elements each reacting on their own. That continuity is
 * the effect; the transition is only what gives it a duration. */
.up-portal-user .up-star-fill {
    width: clamp(0em, calc((var(--rate, 0) - var(--i, 0) + 1) * 1em), 1em);
    overflow: hidden;
    color: var(--ecv-amber, #b07a0c);
    transition: width .22s cubic-bezier(.16, 1, .3, 1), color .18s ease;
}

/* Without the script, --rate is never set and the clamp resolves to 0 — so the classes still
   have to be able to paint the resting value on their own. */
.up-portal-user .up-stars:not([style*="--rate"]) .up-star.is-on .up-star-fill { width: 1em; }
.up-portal-user .up-stars:not([style*="--rate"]) .up-star.is-half .up-star-fill { width: .5em; }

/* Under the pointer: the star being chosen lifts a little and warms, so the value is read from the
   shape as well as from the number beside it. */
.up-portal-user .up-star {
    transition: transform .22s cubic-bezier(.16, 1, .3, 1);
}

/* Only the star under the pointer moves. Lifting the whole row on hover, or lifting
   'everything up to here', fights the fill: the classes still describe the COMMITTED value
   while --rate is showing the previewed one, so any rule reading them mid-hover contradicts
   what the row is drawing. One star, one lift, no argument. */
.up-portal-user .up-star:has(.up-star-hit:hover) {
    transform: translateY(-3px) scale(1.06);
}

.up-portal-user .up-star:has(.up-star-hit:focus-visible) {
    transform: translateY(-3px) scale(1.06);
}

/* In Arabic the stars fill from the right, so the clip has to come from that side too. */
html[dir="rtl"] .up-portal-user .up-star-fill {
    inset-inline-start: auto;
    inset-inline-end: 0;
    direction: rtl;
}

/* The two halves of one star. No background, no border: they are targets, not buttons. */
.up-portal-user .up-star-hit {
    position: absolute;
    inset-block: 0;
    width: 50%;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    z-index: 1;
}

.up-portal-user .up-star-hit.is-half-hit { inset-inline-start: 0; }
.up-portal-user .up-star-hit.is-full-hit { inset-inline-end: 0; }

.up-portal-user .up-stars:hover .up-star-base { color: var(--sys-border-strong, #73787b); }

.up-portal-user .up-star-hit:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 2px;
    border-radius: 3px;
}

.up-portal-user .up-stars.is-readonly .up-star { pointer-events: none; }

/* ------------------------------------------------------------------ *
 * The score, in figures and in words
 * ------------------------------------------------------------------ */

.up-portal-user .up-rating-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 18px;
    margin: 4px 0 12px;
}

.up-portal-user .up-rating-value {
    display: inline-flex;
    align-items: baseline;
    gap: 7px;
    font-variant-numeric: tabular-nums;
}

.up-portal-user .up-rating-value b {
    font-size: clamp(22px, 2.4vw, 28px);
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--sys-text-primary, #17191b);
}

.up-portal-user .up-rating-scale {
    font-size: 13px;
    font-weight: 700;
    color: var(--sys-text-secondary, #73787b);
}

/* The word carries the meaning; it is set apart so the number is not read as a grade. */
.up-portal-user .up-rating-word {
    margin-inline-start: 4px;
    padding: 3px 11px;
    border-radius: 999px;
    background: var(--sys-surface-secondary, var(--ecv-soft, #eff0ee));
    color: var(--sys-text-primary, #17191b);
    font-size: 12px;
    font-weight: 700;
}

.up-portal-user .up-rating-value.is-empty {
    font-size: 13px;
    font-weight: 600;
    color: var(--sys-text-secondary, #73787b);
}


/* ------------------------------------------------------------------ *
 * The prompt a finished ticket ends on
 *
 * It appears the moment a ticket is resolved or closed, directly under the conversation, and
 * it has to read as the natural end of that conversation rather than as an advertisement
 * bolted to the bottom of it. So: the portal's own card, one warm mark, and the score control
 * at full size — this is the only thing being asked for.
 * ------------------------------------------------------------------ */

.up-portal-user .up-feedback-card {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    padding: clamp(18px, 2vw, 26px);
    border-radius: 16px;
    border: 1px solid var(--sys-border, var(--border-color, #e5e6e4));
    background: var(--sys-surface-raised, var(--bg-white, #fff));
    box-shadow: 0 1px 2px rgba(23, 25, 27, .03), 0 14px 36px rgba(23, 25, 27, .06);
}

.up-portal-user .up-feedback-mark {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 14px;
    background: var(--ecv-amber, #b07a0c);
    color: var(--ecv-paper, #fff);
    font-size: 17px;
}

/* Answered: the mark goes green, because the state being reported is "done", not "asked". */
.up-portal-user .up-feedback-card.is-given .up-feedback-mark {
    background: var(--sys-state-success, var(--ecv-green, #13813b));
}

.up-portal-user .up-feedback-body {
    flex: 1 1 auto;
    min-width: 0;
}

.up-portal-user .up-feedback-title {
    display: block;
    font-size: 15.5px;
    font-weight: 800;
    letter-spacing: -.015em;
    color: var(--sys-text-primary, var(--text-color, #17191b));
}

.up-portal-user .up-feedback-lede {
    margin: 5px 0 12px;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
}

.up-portal-user .up-feedback-label {
    display: block;
    margin: 14px 0 6px;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
}

html[dir="rtl"] .up-portal-user .up-feedback-label {
    letter-spacing: normal;
    text-transform: none;
    font-size: 12.5px;
}

.up-portal-user .up-feedback-comment {
    width: 100%;
    box-sizing: border-box;
    padding: 11px 13px;
    border: 1px solid var(--sys-border, var(--border-color, #e5e6e4));
    border-radius: 10px;
    background: var(--sys-surface-page, var(--ecv-canvas, #f5f5f3));
    color: var(--sys-text-primary, var(--text-color, #17191b));
    font: inherit;
    font-size: 13.5px;
    line-height: 1.6;
    resize: vertical;
    transition: border-color .18s ease, box-shadow .18s ease;
}

.up-portal-user .up-feedback-comment:focus {
    outline: none;
    border-color: var(--sys-text-primary, #17191b);
    box-shadow: var(--sys-focus-ring, 0 0 0 4px rgba(23, 25, 27, .10));
}

.up-portal-user .up-feedback-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px 18px;
    margin-top: 14px;
}

.up-portal-user .up-feedback-more {
    font-size: 12px;
    font-weight: 700;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
    text-decoration: none;
}

.up-portal-user .up-feedback-more:hover {
    color: var(--sys-text-primary, var(--text-color, #17191b));
    text-decoration: underline;
}

/* What they said, read back to them. Quoted rather than boxed: it is their sentence. */
.up-portal-user .up-feedback-said {
    margin: 10px 0 0;
    padding-inline-start: 12px;
    border-inline-start: 2px solid var(--sys-border, var(--border-color, #e5e6e4));
    font-size: 13px;
    line-height: 1.6;
    font-style: italic;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
}

@media (max-width: 560px) {
    .up-portal-user .up-feedback-card {
        flex-direction: column;
        gap: 12px;
    }
}


/* ------------------------------------------------------------------ *
 * The prompt, over the ticket
 *
 * The same card as the inline one — it IS the inline one, moved — so nothing about the rating
 * looks different depending on where it is being asked. What the overlay adds is the ground
 * it sits on and the way it arrives.
 * ------------------------------------------------------------------ */

.up-feedback-overlay {
    position: fixed;
    inset: 0;
    z-index: 9500;
    display: grid;
    place-items: center;
    padding: 20px;
    background: var(--sys-overlay, rgba(17, 19, 21, .55));
    animation: up-feedback-veil .28s ease both;
}

@supports (backdrop-filter: blur(6px)) {
    .up-feedback-overlay { backdrop-filter: saturate(1.2) blur(6px); }
}

.up-feedback-overlay[hidden] {
    display: none;
}

.up-feedback-overlay-card {
    position: relative;
    width: min(560px, 100%);
    max-height: min(86svh, 720px);
    overflow-y: auto;
    border-radius: 20px;
    background: var(--sys-surface-raised, var(--bg-white, #fff));
    box-shadow: var(--sys-shadow-float, 0 22px 56px rgba(23, 25, 27, .28));
    animation: up-feedback-rise .34s cubic-bezier(.16, 1, .3, 1) both;
}

/* Inside the overlay the card is the whole dialog, so it drops the border and the lift it
   needs when it is one block among many further down the page. */
.up-feedback-overlay .up-feedback-card {
    border: 0;
    border-radius: 20px;
    box-shadow: none;
}

.up-feedback-close {
    position: absolute;
    inset-block-start: 12px;
    inset-inline-end: 12px;
    z-index: 1;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: 50%;
    background: var(--sys-surface-secondary, var(--ecv-soft, #eff0ee));
    color: var(--sys-text-secondary, var(--text-light, #73787b));
    font-size: 13px;
    cursor: pointer;
    transition: background-color .16s ease, color .16s ease;
}

.up-feedback-close:hover {
    background: var(--sys-surface-hover, #e6e7e4);
    color: var(--sys-text-primary, var(--text-color, #17191b));
}

.up-feedback-close:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 2px;
}

/* The page behind does not scroll while it is being asked. */
body.has-feedback-overlay {
    overflow: hidden;
}

@keyframes up-feedback-veil {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes up-feedback-rise {
    from { opacity: 0; transform: translateY(16px) scale(.97); }
    to { opacity: 1; transform: none; }
}

/* Asked, not sprung. Somebody who has turned motion off gets the same card, present at once. */
@media (prefers-reduced-motion: reduce) {
    .up-feedback-overlay,
    .up-feedback-overlay-card {
        animation: none;
    }
}

/* "Ask me later" is a real choice, so it is a real control — but a quiet one, because the
   rating is what is being asked for and the two must not look like equal options. */
.up-portal-user .up-feedback-later {
    border: 0;
    padding: 0;
    background: none;
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    transition: color .16s ease;
}

.up-portal-user .up-feedback-later:hover {
    color: var(--sys-text-primary, var(--text-color, #17191b));
}

.up-portal-user .up-feedback-later:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 3px;
    border-radius: 3px;
}

/* Showing a rating already given is not a question, so the veil is lighter and the card does
   not hold the page the way an unanswered ask does. */
.up-feedback-overlay.is-answered {
    background: color-mix(in srgb, var(--sys-overlay, rgba(17, 19, 21, .55)) 62%, transparent);
}

@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .up-feedback-overlay.is-answered { background: rgba(17, 19, 21, .34); }
}

/* The score is the subject of that card, so it is given room. */
.up-feedback-overlay .up-rating-row {
    margin-bottom: 4px;
}

/* ------------------------------------------------------------------ *
 * The line at the end of a finished ticket
 *
 * What replaces the form that used to sit here. One row: what it is, and a way into the card.
 * A person who deferred the ask can still reach it, and a person who answered can re-read what
 * they said — without the bottom of every finished ticket carrying a rating form.
 * ------------------------------------------------------------------ */

.up-portal-user .up-feedback-teaser {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    margin-top: 16px;
    padding: 13px 16px;
    border: 1px dashed var(--sys-border, var(--border-color, #e5e6e4));
    border-radius: 12px;
    background: transparent;
    color: var(--sys-text-secondary, var(--text-light, #73787b));
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    text-align: start;
    cursor: pointer;
    transition: border-color .18s ease, color .18s ease, background-color .18s ease;
}

.up-portal-user .up-feedback-teaser:hover {
    border-style: solid;
    border-color: var(--sys-text-primary, var(--text-color, #17191b));
    background: var(--sys-surface-hover, var(--ecv-soft, #eff0ee));
    color: var(--sys-text-primary, var(--text-color, #17191b));
}

.up-portal-user .up-feedback-teaser:focus-visible {
    outline: 2px solid var(--sys-text-primary, #17191b);
    outline-offset: 2px;
}

/* Answered: solid edge and the resolved green, because it is reporting rather than asking. */
.up-portal-user .up-feedback-teaser.is-given {
    border-style: solid;
}

.up-portal-user .up-feedback-teaser.is-given > i:first-child {
    color: var(--sys-state-success, var(--ecv-green, #13813b));
}

.up-portal-user .up-feedback-teaser > span {
    flex: 1 1 auto;
    min-width: 0;
}

.up-portal-user .up-feedback-teaser-go {
    flex: 0 0 auto;
    font-size: 11px;
    opacity: .7;
    transition: transform .18s ease;
}

.up-portal-user .up-feedback-teaser:hover .up-feedback-teaser-go {
    transform: translateX(3px);
}

html[dir="rtl"] .up-portal-user .up-feedback-teaser-go {
    transform: scaleX(-1);
}

html[dir="rtl"] .up-portal-user .up-feedback-teaser:hover .up-feedback-teaser-go {
    transform: scaleX(-1) translateX(3px);
}
