/*
 * Core Reusable Platform - base stylesheet (module 7).
 * Logical properties throughout so this ONE file serves both LTR and RTL -
 * no separate rtl.css, no [dir] overrides needed except where content must
 * visually mirror (icons), which is handled explicitly below.
 *
 * Mobile-first (cross-platform extension): every rule below IS the phone
 * layout - fluid widths, single-column stacks, no fixed desktop-only
 * dimensions. @media (min-width: ...) blocks only ADD room for wider
 * viewports, never fix a mobile bug. Every interactive control (buttons,
 * inputs, links-as-buttons) meets a >=44px touch target. No :hover-only
 * interaction exists anywhere in this stylesheet or its markup - the
 * language switcher, chat widget toggle, and all admin actions are
 * click/tap-triggered, which already works identically with touch.
 */

/* Several widgets (verify-banner, notice screens, ...) toggle visibility
   via the `hidden` attribute on an element that ALSO carries a class
   setting `display: flex/grid/...`. Author rules always beat the
   user-agent's default `[hidden] { display: none }`, so without this
   override those elements stay visibly rendered - as an empty colored
   bar - even while `hidden` is set. Keep this ahead of every other rule. */
[hidden] {
    display: none !important;
}

/* IRANSansWeb (FaNum) - self-hosted, files supplied by the project owner
   under frontend/css/fonts/ (not fetched from any third-party source here,
   see docs/api-contract.md licensing note). Vazirmatn stays as the
   fallback @font-face below it, in case the local files are ever missing
   in a given deploy. */
@font-face {
    font-family: 'IRANSansWeb';
    src: url('fonts/IRANSans.woff2') format('woff2'),
         url('fonts/IRANSans.woff') format('woff');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'IRANSansWeb';
    src: url('fonts/IRANSans-Bold.woff2') format('woff2'),
         url('fonts/IRANSans-Bold.woff') format('woff');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Vazirmatn';
    src: local('Vazirmatn'), local('Vazirmatn-Regular');
    font-display: swap;
}

:root {
    --cp-font-family: 'IRANSansWeb', 'Vazirmatn', system-ui, -apple-system, 'Segoe UI', Tahoma, sans-serif;

    --cp-color-text: #111827;
    --cp-color-text-muted: #6b7280;
    /* --cp-color-bg is the page canvas; --cp-color-surface is anything
       raised above it (main content card, modals, floating panels, header
       bar) - kept distinct so those don't visually disappear into the page. */
    --cp-color-bg: #f3f4f7;
    --cp-color-surface: #ffffff;

    --cp-color-primary: #003366;
    --cp-color-primary-hover: #012a54;
    --cp-color-primary-soft: color-mix(in srgb, #003366 10%, transparent);
    --cp-color-primary-text-on: #ffffff;

    /* Secondary brand hue (design pass 2026-07-29: the all-navy palette
       read as flat/corporate for a platform product) - a teal that sits
       comfortably apart from the semantic success/warning/error hues
       below, used for the brand gradient and accent borders (e.g.
       .cp-package-card's top edge) so not literally everything on screen
       is the same navy blue. */
    --cp-color-accent: #0e8a86;
    --cp-color-accent-hover: #0b6f6c;
    --cp-color-accent-soft: color-mix(in srgb, #0e8a86 12%, transparent);
    --cp-color-accent-text-on: #ffffff;

    /* Formalizes the navy->lighter-blue diagonal gradient that used to be
       hand-written in three separate places (brand-mark x2, auth-side) -
       now the one thing to change if the brand gradient's angle/stops
       ever need adjusting. --cp-gradient-brand-accent pairs primary with
       the new teal accent instead, for the nav's underline and any
       "premium/elevated" surface that wants both brand hues at once. */
    --cp-gradient-brand: linear-gradient(135deg, var(--cp-color-primary), color-mix(in srgb, var(--cp-color-primary) 45%, #2a6fa8));
    --cp-gradient-brand-accent: linear-gradient(90deg, var(--cp-color-primary), var(--cp-color-accent));

    --cp-color-border: #e5e7eb;
    --cp-color-error: #dc2626;
    --cp-color-error-soft: color-mix(in srgb, #dc2626 10%, transparent);
    --cp-color-success: #16a34a;
    --cp-color-success-soft: color-mix(in srgb, #16a34a 10%, transparent);
    --cp-color-warning: #d97706;
    --cp-color-warning-soft: color-mix(in srgb, #d97706 14%, transparent);

    --cp-space-1: 0.25rem;
    --cp-space-2: 0.5rem;
    --cp-space-3: 0.75rem;
    --cp-space-4: 1rem;
    --cp-space-5: 1.5rem;
    --cp-space-6: 2.5rem;

    --cp-radius-sm: 0.4rem;
    --cp-radius: 0.6rem;
    --cp-radius-lg: 1rem;

    --cp-shadow-sm: 0 1px 2px rgba(17, 24, 39, 0.05);
    --cp-shadow: 0 1px 3px rgba(17, 24, 39, 0.04), 0 12px 28px -8px rgba(17, 24, 39, 0.12);
    --cp-shadow-lg: 0 4px 8px rgba(17, 24, 39, 0.05), 0 24px 48px -12px rgba(17, 24, 39, 0.22);

    /* Was 2.75rem (44px, the textbook mobile a11y minimum) - explicit
       product feedback: every button/input on this desktop-first admin
       app read as oversized. 2.25rem (36px) is still a real, comfortably
       clickable target, just not sized for a thumb on a phone. */
    --cp-touch-target: 2.25rem;
}

* {
    box-sizing: border-box;
}

html {
    /* dir/lang are set by frontend/js/i18n/i18n.js, not hardcoded here */
    font-size: 16px;
    /* iOS Safari auto-zooms on input focus if font-size < 16px; every
       control below is explicitly >=16px so that never triggers. */
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: var(--cp-font-family);
    /* Smaller than the 16px root on purpose (module 7 feedback: fit more
       content on screen) - safe to do on body rather than html because
       form controls below set their own font-size: 1rem, which stays tied
       to the ROOT element's 16px regardless of body's size, so the
       iOS-Safari input-zoom guard (see html{} above) is unaffected. */
    font-size: 0.9375rem;
    color: var(--cp-color-text);
    /* A flat --cp-color-bg alone read as bare/empty behind app pages
       (design pass 2026-07-29) - two faint, fixed (not scrolling)
       top-corner tints, same technique .cp-auth-body already used, just
       much lower opacity (2-3% vs 7-10%) so it stays a background detail
       behind dense tables instead of competing with them. background-
       attachment:fixed keeps the wash anchored to the viewport rather
       than repeating/scrolling with page content. */
    background:
        radial-gradient(50rem 26rem at 8% -12%, color-mix(in srgb, var(--cp-color-primary) 3%, transparent), transparent),
        radial-gradient(40rem 22rem at 100% 0%, color-mix(in srgb, var(--cp-color-accent) 3%, transparent), transparent),
        var(--cp-color-bg);
    background-attachment: fixed;
    margin: 0;
    padding: 0 var(--cp-space-3) var(--cp-space-4);
    line-height: 1.6;
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    /* Sticky footer: when page content is shorter than the viewport
       (module spec feedback: short pages on small monitors), #cp-app-footer
       still ends up pinned to the bottom instead of floating right under
       the content - its own margin-block-start:auto (below) consumes all
       the leftover vertical space in this column. Tall content just pushes
       it down normally, no special-casing needed. */
    display: flex;
    flex-direction: column;
}

/* Deliberate type scale - h2/h3/h4 previously fell through to the
   browser's own UA-stylesheet sizes (h2 ~1.5em, h3 ~1.17em of whatever
   the PARENT font-size happened to be), which drifted depending on
   nesting and never matched .cp-container h2's own explicit 1.4rem
   override below. One scale, set once, used everywhere. */
h1, h2, h3, h4 {
    line-height: 1.3;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin-block: 0 var(--cp-space-3);
}

/* Sizes were tuned for a marketing/landing page, not a dense admin app -
   product feedback: h1-h4 were "way" too big and ate real estate a data-
   heavy screen can't spare. Trimmed roughly one step down each. */
h1 {
    font-size: 1.4rem;
}

h2 {
    font-size: 1.15rem;
}

h3 {
    font-size: 0.95rem;
    margin-block-start: var(--cp-space-4);
}

h4 {
    font-size: 0.8rem;
    color: var(--cp-color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* First heading in a card/section shouldn't add extra air above itself -
   the card's own padding already provides that. */
.cp-container > h2:first-child,
.cp-container > h3:first-child,
section > h3:first-child {
    margin-block-start: 0;
}

/* Was capped at 40rem for every page, logged-in dashboards included - a
   real, reported problem: on any normal monitor a whole app screen sat
   in a narrow card with a wall of bare background on both sides. 40rem
   is still exactly right for a login-style FORM (see .cp-auth-body
   .cp-container's own override below, which re-narrows it there) but
   the default now uses the space a real app page has. */
.cp-container {
    /* body is a column flex container (for the sticky-footer trick above),
       and inside flexbox an auto inline margin wins over align-items:stretch
       - without an explicit width the box only ever grows to fit its own
       content instead of filling out to max-width, which is what actually
       produced the narrow-card-with-empty-sides look. */
    width: 100%;
    max-width: 75rem;
    margin-inline: auto;
    margin-block: var(--cp-space-2) var(--cp-space-4);
    background: var(--cp-color-surface);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    box-shadow: var(--cp-shadow);
    padding: var(--cp-space-4);
}

.cp-container--wide {
    max-width: 90rem;
}

.cp-container > p:first-of-type {
    color: var(--cp-color-text-muted);
    margin-block-start: -0.25rem;
}

a {
    color: var(--cp-color-primary);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

/* ---- Full-page centered layout for auth screens (login/register/
   forgot/reset/google-callback) - set via <body class="cp-auth-body"> ---- */

.cp-auth-body {
    /* Only auth pages want body's default top padding back - app pages
       (#cp-app-nav) must stay flush with the viewport top instead, see
       body{} above. */
    padding-block-start: var(--cp-space-4);
    align-items: center;
    background:
        radial-gradient(60rem 30rem at 15% -10%, color-mix(in srgb, var(--cp-color-primary) 10%, transparent), transparent),
        radial-gradient(50rem 25rem at 110% 110%, color-mix(in srgb, var(--cp-color-primary) 7%, transparent), transparent),
        var(--cp-color-bg);
}

.cp-auth-shell {
    display: flex;
    flex-direction: column;
    flex: 1;
    width: 100%;
}

/* Brand panel next to the form on desktop (see the min-width query
   below, where it becomes the full decorative side panel); on mobile
   there's no room for a two-column layout, so this is just a plain
   centered brand row above the form card instead of disappearing
   entirely - moving the brand mark here (2026-08-05 product request)
   means it's no longer "purely decorative" content, so it can't just be
   display:none on small screens the way the old empty panel could. */
.cp-auth-side {
    display: flex;
    align-items: center;
    justify-content: center;
    padding-block: var(--cp-space-4);
}

.cp-auth-side .cp-auth-brand {
    margin-block-end: 0;
}

.cp-auth-body .cp-container {
    max-width: 26rem;
    width: 100%;
    /* Auto top+bottom margin centers the card in whatever space is left
       above the footer (which pins itself to the very bottom via its own
       margin-block-start:auto) - see body{} above for the shared pattern. */
    margin-block: auto;
}

@media (min-width: 60rem) {
    .cp-auth-shell {
        flex-direction: row;
        align-items: stretch;
        justify-content: center;
        max-width: 56rem;
        margin-inline: auto;
        gap: 0;
    }

    .cp-auth-side {
        display: flex;
        align-items: center;
        justify-content: center;
        flex: 1 1 42%;
        min-height: 34rem;
        border-start-start-radius: var(--cp-radius-lg);
        border-end-start-radius: var(--cp-radius-lg);
        background:
            radial-gradient(28rem 20rem at 15% 15%, rgba(255, 255, 255, 0.14), transparent),
            radial-gradient(24rem 18rem at 85% 85%, rgba(255, 255, 255, 0.1), transparent),
            linear-gradient(155deg, var(--cp-color-primary), color-mix(in srgb, var(--cp-color-primary) 45%, #2a6fa8));
    }

    /* Product request (2026-08-05): "عنوان و لوگوی پلتفرم ... به بخش
       مقابل آیتم‌ها (بخش آبی‌رنگ) منتقل شود" - the brand mark moved out
       of the white form card into this dark panel, so it needs its own
       on-dark treatment rather than the default (dark-on-light) styling
       still used everywhere .cp-auth-brand ISN'T on this background. */
    .cp-auth-side .cp-auth-brand {
        margin-block-end: 0;
        color: #fff;
        font-size: 1.35rem;
        gap: var(--cp-space-3);
    }

    .cp-auth-side .cp-auth-brand__mark {
        width: 2.75rem;
        height: 2.75rem;
        font-size: 1.1rem;
        background: rgba(255, 255, 255, 0.16);
        border: 1px solid rgba(255, 255, 255, 0.35);
    }

    [dir="rtl"] .cp-auth-side {
        border-start-start-radius: 0;
        border-start-end-radius: var(--cp-radius-lg);
        border-end-start-radius: 0;
        border-end-end-radius: var(--cp-radius-lg);
    }

    .cp-auth-body .cp-container {
        flex: 1 1 58%;
        max-width: 30rem;
        margin-block: 0;
        border-start-start-radius: 0;
        border-end-start-radius: 0;
        box-shadow: var(--cp-shadow-lg);
    }

    [dir="rtl"] .cp-auth-body .cp-container {
        border-start-start-radius: var(--cp-radius-lg);
        border-start-end-radius: 0;
        border-end-start-radius: var(--cp-radius-lg);
        border-end-end-radius: 0;
    }
}

.cp-auth-brand {
    display: flex;
    align-items: center;
    gap: var(--cp-space-2);
    margin-block-end: var(--cp-space-4);
    font-weight: 700;
    font-size: 1.05rem;
}

.cp-auth-divider {
    display: flex;
    align-items: center;
    gap: var(--cp-space-3);
    margin-block: var(--cp-space-4);
    color: var(--cp-color-text-muted);
    font-size: 0.8rem;
}

.cp-auth-divider::before,
.cp-auth-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--cp-color-border);
}

.cp-auth-google-btn {
    width: 100%;
    background: var(--cp-color-surface);
}

.cp-auth-links {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--cp-space-2);
    margin-block-start: var(--cp-space-4);
    font-size: 0.9rem;
}

.cp-auth-brand__mark {
    width: 2rem;
    height: 2rem;
    border-radius: var(--cp-radius-sm);
    background: var(--cp-gradient-brand);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 0.9rem;
    flex-shrink: 0;
}

button,
input,
select,
textarea {
    /* Base (mobile) tap-target floor for actual form controls/buttons.
       Plain inline links are left alone (min-height is inert on inline
       elements anyway); anywhere a link acts as a button it's made
       inline-flex explicitly, see .cp-chat-widget__suggested-action etc. */
    min-height: var(--cp-touch-target);
    font-size: 1rem;
    font-family: inherit;
}

button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-inline: var(--cp-space-4);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius);
    background: var(--cp-color-surface);
    color: var(--cp-color-text);
    cursor: pointer;
    font-weight: 600;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
}

button:hover {
    background: color-mix(in srgb, var(--cp-color-text) 4%, var(--cp-color-surface));
    box-shadow: var(--cp-shadow-sm);
}

button:active {
    transform: translateY(1px);
}

button:disabled {
    opacity: 0.55;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

/* Gradient instead of a flat fill (design pass 2026-07-29) - the primary
   action on every form/page deserves to look like the platform's most
   important control, not the same flat navy rectangle as everything
   else. Hover swaps the GLOW (box-shadow), not the gradient itself, since
   `background` (a gradient) doesn't itself transition smoothly the way
   background-color would - the existing transition list above already
   covers box-shadow. */
button[type="submit"] {
    border: none;
    background: var(--cp-gradient-brand);
    color: var(--cp-color-primary-text-on);
    box-shadow: var(--cp-shadow-sm), 0 2px 10px -4px color-mix(in srgb, var(--cp-color-primary) 45%, transparent);
}

button[type="submit"]:hover {
    box-shadow: var(--cp-shadow-sm), 0 6px 18px -4px color-mix(in srgb, var(--cp-color-primary) 60%, transparent);
}

/* Red primary-weight submit button for a form whose whole purpose is
   destructive (account deletion, etc.) - product feedback (2026-07-30):
   "دکمه‌های submit فرم‌های حذف‌کننده را هم قرمز کن". A plain .cp-btn-danger
   class alone can't win here since button[type="submit"] above already
   sets its own background/color at equal specificity (element+attribute
   vs. a single class) - this compound selector is deliberately more
   specific so it overrides the navy gradient for exactly this case,
   while every other type="submit" button keeps the brand gradient. */
button[type="submit"].cp-btn-danger {
    background: var(--cp-color-error);
    color: #fff;
    box-shadow: var(--cp-shadow-sm), 0 2px 10px -4px color-mix(in srgb, var(--cp-color-error) 45%, transparent);
}

button[type="submit"].cp-btn-danger:hover {
    background: color-mix(in srgb, var(--cp-color-error) 85%, black);
    box-shadow: var(--cp-shadow-sm), 0 6px 18px -4px color-mix(in srgb, var(--cp-color-error) 60%, transparent);
}

@media (min-width: 48rem) {
    body {
        padding: 0 var(--cp-space-4) var(--cp-space-6);
    }

    .cp-container {
        padding: var(--cp-space-5);
        margin-block: var(--cp-space-5);
    }

    .cp-auth-body {
        padding-block-start: var(--cp-space-6);
    }
}

/* ---- Language switcher ---- */

/* Round icon-style badges (design request) - previously styled a
   `button` selector that didn't match the actual markup (plain `<a>`
   tags), so this had never actually rendered; the language links showed
   as bare underlined text. Content is now a short 2-letter code (FA/AR/EN)
   sized to sit inside a circle, with the full name in `title` for a
   hover tooltip/screen readers. */
.cp-lang-switcher {
    display: flex;
    justify-content: flex-end;
    gap: var(--cp-space-2);
    margin-block-end: var(--cp-space-4);
}

.cp-lang-switcher a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    border: 1px solid var(--cp-color-border);
    background: var(--cp-color-surface);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--cp-color-text-muted);
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.cp-lang-switcher a:hover {
    background: var(--cp-color-primary-soft);
    color: var(--cp-color-primary);
    text-decoration: none;
}

.cp-lang-switcher a[aria-current="true"] {
    background: var(--cp-color-primary);
    color: var(--cp-color-primary-text-on);
    border-color: var(--cp-color-primary);
}

/* ---- Dynamic form (module 4 sample) ---- */

.cp-dynamic-form {
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-4);
}

.cp-form-field {
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-1);
}

.cp-form-field label {
    font-weight: 600;
    font-size: 0.8rem;
    letter-spacing: 0.01em;
    color: var(--cp-color-text-muted);
    text-align: start;
}

.cp-form-field input,
.cp-form-field select,
.cp-form-field textarea {
    font-family: inherit;
    font-size: 1rem;
    padding: var(--cp-space-2) var(--cp-space-3);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius);
    text-align: start;
    background: color-mix(in srgb, var(--cp-color-bg) 55%, var(--cp-color-surface));
    color: var(--cp-color-text);
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
    /* A field inside a flex-column .cp-form-field stretches to fill the
       full column width by default (flex align-items:stretch) - fine for
       a single-field auth form, but inside a wide admin card a plain text
       input stretching the full 60-75rem container width reads as broken,
       not intentional. Cap it to a size proportionate to what it actually
       holds; a handful of field kinds override this below.

       Product feedback (2026-08-05, two rounds): first pass gave
       password/email each their OWN narrower width (14rem/18rem, "طول
       آیتم باید متناسب با کاربرد آن باشد ... هیچ کلمه عبوری اینقدر بلند
       نیست"), but a same-day follow-up asked for the OPPOSITE within a
       form - "چون فقط دو ایتم هستند اگر برابر باشند فرم زیباتر است ...
       نه فقط در فرم لاگین" (when a form has a small number of same-kind
       fields, EQUAL width reads better - not just login specifically).
       Net result: one shared, moderate width (18rem, down from the
       original 22rem default) for every plain text-like input - narrower
       than before so a password field doesn't look absurdly long, but
       uniform so email/password/name/code fields in the same form all
       line up instead of stair-stepping by type. number/search/textarea
       still override below - those differ for functional reasons (a
       number is inherently short; a search box and a textarea both
       genuinely need more room), not the "make it feel intentional"
       reasoning that applied to password/email. */
    max-width: 18rem;
    width: 100%;
}

.cp-form-field textarea {
    max-width: 28rem;
}

.cp-form-field input[type="number"] {
    max-width: 7rem;
}

.cp-form-field input[type="search"] {
    max-width: none;
}

/* A language select only ever holds 3 short options (فارسی/العربية/
   English) - the shared 18rem default read as oversized for it (product
   feedback 2026-08-01: "طول کمبوباکس زبان کاربری را نصف کنید"), same
   functional-reason exception as number/search above. */
select[name="locale"] {
    max-width: 9rem;
}

/* Label beside its control instead of stacked above it (product request:
   "items should sit in front of their caption, in every form") - kept
   behind a min-width query rather than the default, since this app is
   mobile-first (see the file header comment) and a fixed-width label
   column has no room on a phone; the base (mobile) rule above still
   stacks label-then-control there. .cp-form-field--checkbox is excluded -
   its single <label> already wraps the checkbox+text as one unit, so
   forcing a label/control split here would break that layout.
   .cp-inline-form is also excluded from THIS rule specifically because it
   has its own row rule below (2026-08-01 fix - a field inside
   .cp-inline-form used to stack label-above-input internally, which is
   exactly the "caption below/above its item" layout the platform-wide
   convention exists to prevent; product feedback caught a live instance
   of this on admin-organizations.php's package-assignment field, but the
   same .cp-inline-form rule applies everywhere it's used).

   PLATFORM-WIDE CONVENTION (2026-08-05, product request: "این الگو را در
   تمام فرم‌ها ... حتی فرم‌هایی که بعداً اضافه می‌شوند ... گسترش دهید" -
   every current AND FUTURE form on the platform should get this, without
   the page author doing anything special beyond using the existing
   .cp-dynamic-form/.cp-form-field markup convention that's already
   standard practice here). Two cases:
   1. A .cp-form-field with a .cp-dynamic-form ancestor: CSS GRID, one
      shared label-column width across every field in that SAME form (see
      the .cp-dynamic-form rule below) - a content-sized column, not one
      fixed guess-width for every form on the platform regardless of what
      its labels actually say.
   2. A standalone .cp-form-field (no .cp-dynamic-form ancestor, e.g. a
      lone filter dropdown) - just below - has nothing else on the page to
      align its column WITH, so it gets a simple auto-width flex row
      instead; Grid's whole benefit is moot for a single isolated field. */
@media (min-width: 48rem) {
    /* Form-field text reads oversized next to the rest of the platform's
       UI text (product feedback 2026-08-01: "فونت متن داخل آیتمها در تمام
       نرم‌افزار یک اندازه شود و الان زیادی بزرگ است"). Deliberately NOT
       touched in the base (mobile) rule above - that 1rem is pinned to the
       ROOT element specifically to stay >=16px and avoid iOS Safari's
       auto-zoom-on-input-focus bug (see html{}'s own comment) - this
       override only applies once that's no longer a concern (desktop/
       tablet width), bringing fields down to the same size used platform-
       wide for buttons/labels/body text instead of the browser default. */
    .cp-form-field input,
    .cp-form-field select,
    .cp-form-field textarea {
        font-size: 0.9rem;
    }

    .cp-form-field:not(.cp-form-field--checkbox):not(.cp-inline-form *):not(.cp-dynamic-form *) {
        flex-direction: row;
        align-items: center;
        gap: var(--cp-space-4);
    }

    .cp-form-field:not(.cp-form-field--checkbox):not(.cp-inline-form *):not(.cp-dynamic-form *) > label {
        flex: 0 0 auto;
        white-space: nowrap;
        margin: 0;
    }

    .cp-form-field:not(.cp-form-field--checkbox):not(.cp-inline-form *):not(.cp-dynamic-form *) > input,
    .cp-form-field:not(.cp-form-field--checkbox):not(.cp-inline-form *):not(.cp-dynamic-form *) > select,
    .cp-form-field:not(.cp-form-field--checkbox):not(.cp-inline-form *):not(.cp-dynamic-form *) > textarea {
        flex: 1 1 auto;
        min-width: 0;
    }

    .cp-inline-form .cp-form-field {
        flex-direction: row;
        align-items: center;
    }

    .cp-inline-form .cp-form-field > label {
        flex: none;
        white-space: nowrap;
    }

    /* Case 1 - see the platform-wide convention note above. The label
       column sizes itself up to the WIDEST label actually present on
       THIS form (a short login form gets a narrow column; a form with
       "نام و نام خانوادگی" gets a wider one) while every row in the SAME
       form still shares that one column - real alignment, not a guess.
       Capped at 16rem, not plain max-content: admin-settings.php's labels
       ("مهلت تأیید پرداخت پس از پایان اشتراک (ساعت)") are full sentences,
       not short captions - an uncapped max-content column sized itself to
       that whole string, leaving the 1fr input column almost nothing
       (measured: went negative, whole grid broke). Past the cap, a label
       WRAPS onto 2 lines (see the label rule below removing nowrap)
       instead of forcing the column wider.

       NOTE the function used: fit-content(16rem) - CSS Grid's dedicated
       function for exactly this ("size to content, but never past this
       cap"), not a hand-rolled substitute. Two substitutes were tried and
       both broke, caught only by testing an actual SHORT-label form
       (login.php) after the LONG-label form (admin-settings.php) already
       looked right - a reminder that "it works on the page I built it
       for" isn't proof for a platform-wide rule:
         - minmax(min-content, 16rem): a grid track range, not a content-
           fit. Grid gives a non-flexible track its full MAXIMUM whenever
           there's spare space to hand out (which a short login form
           always has) - so login's short "ایمیل"/"رمز عبور" pair still
           got a full 16rem column, silently reintroducing the exact
           "wastes space for a short label" complaint this mechanism
           exists to fix.
         - min(max-content, 16rem): max-content is a grid/flex sizing
           KEYWORD, not a length - CSS's min()/max() math functions only
           reliably operate on actual lengths, so this doesn't compute as
           "content, capped at 16rem" the way it reads; browsers fell
           back to auto-like full-width sizing (measured: label stretched
           to the ENTIRE form width, worse than the first bug). */
    .cp-dynamic-form {
        display: grid;
        grid-template-columns: fit-content(16rem) 1fr;
        align-items: center;
        gap: var(--cp-space-4) 1.5rem;
    }

    .cp-dynamic-form .cp-form-field:not(.cp-form-field--checkbox) {
        display: contents;
    }

    /* Opt-in escape hatch: a wrapper element sitting between
       .cp-dynamic-form and its fields (a conditionally-shown step, a
       grouped section) needs to be transparent to the grid too, or it
       becomes one opaque grid cell instead of letting its .cp-form-field
       children through - give it this class. <fieldset> is covered by
       tag alone since nothing in this app puts visual styling on a
       fieldset's own box. */
    .cp-dynamic-form .cp-fields-wrap,
    .cp-dynamic-form fieldset {
        display: contents;
    }

    .cp-dynamic-form legend {
        grid-column: 1 / -1;
    }

    /* Catch-all, not an enumerated list: ANY direct child of
       .cp-dynamic-form that isn't dissolved into the grid above (a plain
       field or one of the wrapper escape hatches) spans the full row -
       covers the already-known cases (checkbox fields, the submit button,
       .cp-form-message) AND ones found the hard way (admin-settings.php
       has <p class="cp-form-hint"> paragraphs BETWEEN fields as direct
       .cp-dynamic-form children - unhandled, that shifted every field
       after the first hint into the wrong column entirely, since default
       grid auto-placement just drops an unspanned item into the next
       free cell in sequence) - and any future element type someone adds
       without needing this list updated. */
    .cp-dynamic-form > *:not(.cp-fields-wrap):not(fieldset):not(.cp-form-field:not(.cp-form-field--checkbox)) {
        grid-column: 1 / -1;
    }

    /* QA follow-up (2026-08-05): spanning both columns (above) is about
       POSITION, not width - a grid item's default is to STRETCH to fill
       its full spanned area, which turned the submit button into a
       giant edge-to-edge bar (profile.php's "ذخیرهٔ تغییرات" spanned the
       whole card). Buttons should size to their own label/icon like
       every button elsewhere in the app, just started at the reading-
       start side of the row it's on. */
    .cp-dynamic-form > button {
        justify-self: start;
    }

    .cp-dynamic-form .cp-form-field:not(.cp-form-field--checkbox) > label {
        margin: 0;
    }

    /* A field with no label at all (a bare reply-box textarea, an
       action-button row) has nothing to pair with a label column - span
       the full row instead of being squeezed into it. */
    .cp-dynamic-form .cp-form-field:not(:has(label)) {
        display: block;
        grid-column: 1 / -1;
    }

    /* Anything beyond label/input/select/textarea inside a field (a hint
       line, a password-strength meter) spans the full row rather than
       landing in the narrow label column - display is untouched here
       (unlike the wrapper escape hatch above), so these keep their own
       visual box/background/etc.
       EXCEPT .cp-form-field__value (design feedback 2026-08-01: a JS-
       built custom control standing in for a real <input> - e.g. a
       toggle switch - must sit BESIDE its label in the value column like
       any other control, not stack below it as if it were a hint). */
    .cp-dynamic-form .cp-form-field > *:not(label):not(input):not(select):not(textarea):not(.cp-form-field__value) {
        grid-column: 1 / -1;
    }

    /* A textarea is always full-width - a multi-line control doesn't
       pair naturally beside a label the way a single-line input does. */
    .cp-dynamic-form .cp-form-field textarea {
        grid-column: 1 / -1;
    }
}

/* ---- Jalali (Solar Hijri) date picker (frontend/js/widgets/
   jalali-datepicker.js) - only mounted when locale is 'fa'. ---- */
.cp-jalali-picker {
    position: relative;
    display: inline-block;
    width: 100%;
}

.cp-jalali-picker__panel {
    position: absolute;
    z-index: 50;
    inset-inline-start: 0;
    top: calc(100% + 0.25rem);
    background: var(--cp-color-surface);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius);
    box-shadow: var(--cp-shadow-lg);
    padding: var(--cp-space-2);
    width: 16rem;
}

.cp-jalali-picker__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-block-end: var(--cp-space-2);
}

.cp-jalali-picker__month-label {
    font-weight: 600;
    font-size: 0.85rem;
}

.cp-jalali-picker__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 0.15rem;
    text-align: center;
}

.cp-jalali-picker__grid--weekdays {
    margin-block-end: 0.25rem;
    font-size: 0.7rem;
    color: var(--cp-color-text-muted);
}

.cp-jalali-picker__day {
    min-height: 2rem;
    padding: 0;
    border: none;
    background: transparent;
    font-size: 0.8rem;
    border-radius: var(--cp-radius-sm);
}

.cp-jalali-picker__day:hover {
    background: var(--cp-color-primary-soft);
}

.cp-jalali-picker__day--today {
    border: 1px solid var(--cp-color-primary);
}

/* ---- Compact inline form row: a couple of small fields (e.g. two number
   inputs) that don't need their own full-height labeled-stack section -
   label sits beside the field instead of above it, everything on one
   line, smaller footprint. Use instead of .cp-dynamic-form when the whole
   form is 2-3 short fields plus a submit button. ---- */
.cp-inline-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cp-space-3);
    margin-block-end: var(--cp-space-4);
}

.cp-inline-form .cp-form-field {
    gap: var(--cp-space-2);
}

.cp-inline-form .cp-form-field label {
    font-size: 0.7rem;
}

.cp-inline-form input {
    max-width: 6rem;
}

.cp-inline-form button[type="submit"] {
    margin-block-start: 0;
}

.cp-form-field input:focus,
.cp-form-field select:focus,
.cp-form-field textarea:focus {
    outline: none;
    border-color: var(--cp-color-primary);
    background: var(--cp-color-surface);
    box-shadow: 0 0 0 3px var(--cp-color-primary-soft);
}

.cp-dynamic-form button[type="submit"] {
    align-self: start;
    padding-block: var(--cp-space-2);
    margin-block-start: var(--cp-space-1);
}

.cp-form-field--checkbox label {
    display: flex;
    align-items: center;
    gap: var(--cp-space-2);
    font-weight: 500;
    font-size: 0.9rem;
    color: var(--cp-color-text);
}

.cp-form-field--checkbox input[type="checkbox"] {
    min-height: auto;
    width: 1.1rem;
    height: 1.1rem;
    accent-color: var(--cp-color-primary);
}

.cp-form-message {
    font-size: 0.9rem;
    font-weight: 500;
}

.cp-form-message--error {
    color: var(--cp-color-error);
}

.cp-form-message--success {
    color: var(--cp-color-success);
}

/* ---- Admin signup-risk review (free-trial anti-abuse extension) ---- */

.cp-risk-list {
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-3);
}

/* Tightened (product feedback: log/record cards across the admin pages -
   signup-risk, payment review, analytics-by-package, announcement/
   promotion/organization stats - carried too much visual weight at the
   default body font-size for what is essentially secondary/diagnostic
   info, not primary content). font-size shrinks the whole card (header
   included) closer to .cp-table's own density; .cp-signal-list below
   goes a step further since it's the least important tier (raw signal
   detail lines). */
.cp-risk-card {
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    padding: var(--cp-space-2) var(--cp-space-3);
    background: var(--cp-color-surface);
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-1);
    font-size: 0.85rem;
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.cp-risk-card:hover {
    box-shadow: var(--cp-shadow);
    border-color: var(--cp-color-primary-soft);
}

.cp-risk-card__header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cp-space-2);
}

.cp-risk-card__header strong {
    font-weight: 700;
}

.cp-risk-card__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cp-space-2);
    margin-block-start: var(--cp-space-1);
}

/* A .cp-risk-card__actions nested directly inside .cp-risk-card__header
   (no separate body content between them, e.g. admin-organizations.php's
   pending-member cards) sits at the row's end instead of on its own line
   below - design feedback (2026-07-30): "دکمه‌ها جلوی متن باشند نه پایین
   آن" (buttons should sit alongside the content, not below it). */
.cp-risk-card__header > .cp-risk-card__actions {
    margin-inline-start: auto;
    margin-block-start: 0;
}

/* Body content (a paragraph or .cp-signal-list) sharing one row with its
   actions instead of the actions wrapping to their own line below -
   same feedback, for cards that DO have separate body content between
   the header and the actions. */
.cp-risk-card__body-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--cp-space-3);
}

.cp-risk-card__body-row > :first-child {
    flex: 1 1 16rem;
    min-width: 0;
}

.cp-risk-card__body-row > .cp-risk-card__actions {
    flex-shrink: 0;
    margin-block-start: 0;
}

.cp-signal-list {
    margin: 0;
    padding-inline-start: var(--cp-space-4);
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--cp-color-text-muted);
}

/* ---- Data table (admin lists at real scale) ----
   Row-per-record cards (.cp-risk-card) work well for a handful of items
   with lots of per-item detail, but a flat list of users/organizations is
   tabular data - one name, one status, one action - and stacking those in
   full-width cards wastes exactly the horizontal space a wide admin
   screen has, forcing the eye to re-scan each card's internal layout
   instead of reading straight down an aligned column. This is the
   standard admin-panel pattern instead: a real <table>, one row per
   record, columns that stay aligned down the page. */
.cp-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    background: var(--cp-color-surface);
}

.cp-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
    white-space: nowrap;
}

.cp-table th {
    text-align: start;
    padding: var(--cp-space-3) var(--cp-space-4);
    background: color-mix(in srgb, var(--cp-color-text) 3%, var(--cp-color-surface));
    color: var(--cp-color-text-muted);
    font-weight: 600;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    border-bottom: 1px solid var(--cp-color-border);
}

.cp-table td {
    padding: var(--cp-space-3) var(--cp-space-4);
    border-bottom: 1px solid var(--cp-color-border);
    vertical-align: middle;
    white-space: normal;
}

.cp-table tbody tr:last-child td {
    border-bottom: none;
}

.cp-table tbody tr:hover {
    background: color-mix(in srgb, var(--cp-color-text) 3%, transparent);
}

.cp-table__primary {
    font-weight: 600;
    color: var(--cp-color-text);
}

/* Compact variant for pure log/event tables (admin-console.php's per-user
   activity list, potentially many short rows of low-stakes diagnostic
   data) - smaller text and tighter row padding than the default .cp-table,
   which is sized for primary record lists (users/organizations) where
   each row matters more individually. */
.cp-table--compact {
    font-size: 0.8rem;
}

.cp-table--compact th,
.cp-table--compact td {
    padding: var(--cp-space-2) var(--cp-space-3);
}

.cp-table__secondary {
    display: block;
    color: var(--cp-color-text-muted);
    font-size: 0.8rem;
    margin-block-start: 0.15rem;
}

.cp-table__actions {
    display: flex;
    gap: var(--cp-space-2);
    justify-content: flex-end;
    flex-wrap: nowrap;
}

/* Same recipe as .cp-btn-sm below (kept as one shared rule, not two
   independent copies - a design-consistency pass 2026-08-05 found this
   exact ruleset hand-duplicated in 3+ places across the file with no
   shared class, silent drift waiting to happen the next time only one
   copy got tweaked). white-space:nowrap is table-action-specific (a
   dense row is more likely to force an awkward mid-label wrap than a
   free-standing button elsewhere), so it stays scoped here. */
.cp-table__actions button,
.cp-table__actions a {
    white-space: nowrap;
}

.cp-table__empty td {
    padding: var(--cp-space-6) var(--cp-space-4);
    text-align: center;
    color: var(--cp-color-text-muted);
}

/* ---- Toolbar above a table: search + primary action, side by side
   instead of a form that permanently pushes the list further down the
   page every time the section is opened. ---- */
.cp-list-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cp-space-3);
    align-items: center;
    margin-block-end: var(--cp-space-4);
}

.cp-list-toolbar__search {
    flex: 1 1 16rem;
    min-width: 0;
}

.cp-list-toolbar input[type="search"] {
    width: 100%;
}

/* ---- Generic report/list toolbar (frontend/js/widgets/report-table.js) -
   the platform-wide convention for "search on the reading-start side
   (right in RTL), a small summary-stats block on the reading-end side
   (left in RTL, ~20% width)" that sits above any paginated table. DOM
   order is filter-first/stats-second, which already lands correctly in
   RTL without any explicit reordering. ---- */
.cp-report-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cp-space-3);
    align-items: stretch;
    margin-block-end: var(--cp-space-4);
}

.cp-report-toolbar__filter {
    flex: 1 1 65%;
    min-width: 12rem;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cp-space-2);
    align-items: center;
}

/* Same checkbox+label recipe as .cp-form-field--checkbox label - an
   opt-in extra filter control (e.g. "show inactive/archived too") beside
   a report's search box, see report-table.js's opts.extraFilter. */
.cp-report-toolbar__toggle {
    display: flex;
    align-items: center;
    gap: var(--cp-space-2);
    font-size: 0.85rem;
    color: var(--cp-color-text-muted);
    white-space: nowrap;
}

.cp-report-toolbar__toggle input[type="checkbox"] {
    min-height: auto;
    width: 1.1rem;
    height: 1.1rem;
    accent-color: var(--cp-color-primary);
}

.cp-report-toolbar__search {
    flex: 1 1 16rem;
    min-width: 0;
    width: 100%;
}

.cp-report-toolbar__stats {
    flex: 1 1 9rem;
    max-width: 20%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.2rem;
    padding-inline-start: var(--cp-space-3);
    border-inline-start: 1px solid var(--cp-color-border);
}

.cp-report-toolbar__stat-value {
    font-weight: 700;
}

.cp-report-toolbar__stat-label {
    font-size: 0.7rem;
    color: var(--cp-color-text-muted);
}

@media (max-width: 30rem) {
    .cp-report-toolbar__stats {
        max-width: none;
        border-inline-start: none;
        padding-inline-start: 0;
        border-block-start: 1px solid var(--cp-color-border);
        padding-block-start: var(--cp-space-2);
    }
}

/* Compact, size-neutral button variants for dense contexts (table
   action cells, inline toolbars) where the default 44px touch-target
   button reads as oversized. */
.cp-btn-sm {
    min-height: 2.1rem;
    padding-inline: var(--cp-space-3);
    font-size: 0.8rem;
}

/* A plain <a> used as a table-action "button" (e.g. "manage members")
   has none of <button>'s default chrome - give it the same look so it
   doesn't read as a stray underlined link sitting next to real buttons. */
a.cp-btn-sm {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius);
    background: var(--cp-color-surface);
    color: var(--cp-color-text);
    text-decoration: none;
}

a.cp-btn-sm:hover {
    background: color-mix(in srgb, var(--cp-color-text) 4%, var(--cp-color-surface));
    text-decoration: none;
}

.cp-btn-danger {
    border-color: color-mix(in srgb, var(--cp-color-error) 40%, var(--cp-color-border));
    color: var(--cp-color-error);
    background: var(--cp-color-surface);
}

.cp-btn-danger:hover {
    background: var(--cp-color-error-soft);
}

/* ---- Semantic hover colors (design request 2026-07-29): a button's
   color should hint at what it DOES, not just wash grey on hover like
   every other button. .cp-btn-danger above already does this for
   destructive actions (red, colored at rest too since "this will delete
   something" deserves to stand out even before the pointer arrives); the
   three added here stay neutral at rest and only shift color on
   hover/focus, since "confirm"/"cancel"/"informational" aren't
   inherently alarming the way "delete" is:
     - .cp-btn-success  - green  - confirms/creates/approves/activates/
                                    saves something (ثبت، تأیید، فعال‌سازی)
     - .cp-btn-cancel   - amber  - backs out of a form/action without
                                    completing it (لغو، انصراف)
     - .cp-btn-info     - teal   - a neutral, non-destructive action that
                                    just navigates or reveals more detail
                                    (مشاهده، جزئیات، مدیریت)
   Same --cp-color-success/warning/accent tokens already used for badges
   and the trial banner elsewhere, so this doesn't introduce new colors to
   the palette - just a new place they're applied. focus-visible mirrors
   :hover so keyboard users get the same cue. ---- */
.cp-btn-success:hover,
.cp-btn-success:focus-visible {
    background: var(--cp-color-success-soft);
    border-color: var(--cp-color-success);
    color: var(--cp-color-success);
}

.cp-btn-cancel:hover,
.cp-btn-cancel:focus-visible {
    background: var(--cp-color-warning-soft);
    border-color: var(--cp-color-warning);
    color: var(--cp-color-warning);
}

.cp-btn-info:hover,
.cp-btn-info:focus-visible {
    background: var(--cp-color-accent-soft);
    border-color: var(--cp-color-accent);
    color: var(--cp-color-accent);
}

.cp-btn-icon.cp-btn-success:hover svg,
.cp-btn-icon.cp-btn-success:focus-visible svg {
    color: var(--cp-color-success);
}

.cp-btn-icon.cp-btn-cancel:hover svg,
.cp-btn-icon.cp-btn-cancel:focus-visible svg {
    color: var(--cp-color-warning);
}

.cp-btn-icon.cp-btn-info:hover svg,
.cp-btn-icon.cp-btn-info:focus-visible svg {
    color: var(--cp-color-accent);
}

/* Icon + label button (see frontend/js/icons.js's iconButton() helper) -
   the icon is fill="currentColor", so recoloring it on hover is just a
   CSS `color` change, no separate hover-state asset and correct in dark
   mode automatically (same trick the nav icons already used). */
.cp-btn-icon {
    display: inline-flex;
    align-items: center;
    gap: var(--cp-space-2);
    min-height: var(--cp-touch-target);
    padding-inline: var(--cp-space-3);
}

.cp-btn-icon.cp-btn-sm {
    gap: 0.35rem;
}

.cp-btn-icon svg {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    transition: color 0.15s ease;
}

.cp-btn-icon:hover svg {
    color: var(--cp-color-primary);
}

.cp-btn-icon.cp-btn-danger:hover svg {
    color: var(--cp-color-error);
}

/* Icon-only square button (copy-to-clipboard next to a code, etc.) - no
   visible label, relies on `title` for a native tooltip. */
.cp-icon-only-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.1rem;
    min-height: 2.1rem;
    padding: 0;
    flex-shrink: 0;
}

.cp-icon-only-btn svg {
    width: 1rem;
    height: 1rem;
    transition: color 0.15s ease;
}

.cp-icon-only-btn:hover svg {
    color: var(--cp-color-primary);
}

/* Value + inline copy button, used anywhere a code/id is shown for the
   user to hand off elsewhere (invite codes, organization code). */
.cp-copyable {
    display: inline-flex;
    align-items: center;
    gap: var(--cp-space-2);
}

/* ---- GDPR/data-use info tooltip (frontend/js/icons.js's infoTooltip()) -
   next to any field/form that collects personal data. ---- */
.cp-info-tip {
    position: relative;
    display: inline-flex;
    vertical-align: middle;
    margin-inline-start: 0.3rem;
}

.cp-info-tip__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    min-width: 0;
    min-height: 0;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--cp-color-text-muted);
    cursor: help;
}

.cp-info-tip__icon svg {
    width: 1rem;
    height: 1rem;
}

.cp-info-tip__icon:hover {
    color: var(--cp-color-primary);
}

.cp-info-tip__pop {
    position: absolute;
    z-index: 60;
    top: calc(100% + 0.35rem);
    inset-inline-start: 0;
    width: 16rem;
    max-width: 70vw;
    background: var(--cp-color-text);
    color: var(--cp-color-surface);
    font-size: 0.75rem;
    line-height: 1.5;
    padding: var(--cp-space-2) var(--cp-space-3);
    border-radius: var(--cp-radius);
    box-shadow: var(--cp-shadow-lg);
}

/* ---- Toast (frontend/js/widgets/toast.js) - transient success/error
   feedback for row actions (approve/reject/revoke/delete/toggle) that
   don't have their own dedicated .cp-form-message. ---- */
.cp-toast-container {
    position: fixed;
    bottom: var(--cp-space-4);
    left: 50%;
    transform: translateX(-50%);
    z-index: 2000;
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-2);
    align-items: center;
    pointer-events: none;
}

.cp-toast {
    padding: var(--cp-space-2) var(--cp-space-4);
    border-radius: var(--cp-radius);
    box-shadow: var(--cp-shadow-lg);
    font-size: 0.85rem;
    color: #ffffff;
    opacity: 1;
    transition: opacity 0.25s ease, transform 0.25s ease;
}

.cp-toast--success {
    background: var(--cp-color-success);
}

.cp-toast--error {
    background: var(--cp-color-error);
}

.cp-toast--out {
    opacity: 0;
    transform: translateY(0.5rem);
}

/* A submit button is already solid-primary with light text/icon by
   default (button[type="submit"] above) - no separate icon recolor on
   hover, the .cp-btn-icon:hover rule above would otherwise fight it. */
.cp-btn-icon[type="submit"]:hover svg {
    color: inherit;
}

/* Was referenced by six different "reject/delete/revoke" buttons across
   the app with no matching rule at all - every one of them silently
   rendered as a plain default button, indistinguishable from a neutral
   action. Now genuinely styled as a quiet, non-primary "dismiss" button -
   the one place that's actually correct (chat-widget.js's own Cancel
   button); destructive table actions use .cp-btn-danger above instead. */
.cp-chat-widget__cancel {
    background: transparent;
    color: var(--cp-color-text-muted);
}

.cp-badge {
    display: inline-block;
    padding-inline: var(--cp-space-2);
    padding-block: 0.15rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    /* A 1px ring in the badge's own color (design pass 2026-07-29) reads
       as a deliberate pill/chip rather than a flat rectangle-with-radius -
       color is set per-variant below via currentColor so this one rule
       covers every variant, including any future .cp-badge--* added
       later without needing its own box-shadow line. */
    box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent);
}

.cp-badge--flagged,
.cp-badge--warning {
    background: var(--cp-color-warning-soft);
    color: var(--cp-color-warning);
}

.cp-badge--blocked,
.cp-badge--critical {
    background: var(--cp-color-error-soft);
    color: var(--cp-color-error);
}

.cp-badge--info {
    background: var(--cp-color-primary-soft);
    color: var(--cp-color-primary);
}

/* ---- Trial state banner (visible everywhere per spec) ---- */

.cp-trial-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--cp-space-3);
    padding: var(--cp-space-3) var(--cp-space-4);
    border-radius: var(--cp-radius);
    background: linear-gradient(135deg, var(--cp-color-primary-soft), color-mix(in srgb, var(--cp-color-primary) 4%, transparent));
    border: 1px solid var(--cp-color-primary-soft);
    margin-block-end: var(--cp-space-4);
}

/* Compact-CTA padding shared with .cp-package-card button below (kept as
   one grouped rule, not two hand-copied ones) - the two differ only in
   background/color, not footprint. */
.cp-trial-banner__upgrade,
.cp-package-card button {
    padding-inline: var(--cp-space-3);
    padding-block: var(--cp-space-2);
    border: none;
}

.cp-trial-banner__upgrade {
    background: var(--cp-color-primary);
    color: var(--cp-color-primary-text-on);
}

/* ---- Billing / package catalog ---- */

.cp-package-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: var(--cp-space-3);
}

.cp-package-card {
    border: 1px solid var(--cp-color-border);
    /* Fixed accent top edge (design pass 2026-07-29) - stays the accent
       teal regardless of hover state, see the hover rule below which only
       ever touches the other 3 sides via border-inline-color/border-
       block-end-color so this doesn't get overwritten by a `border-color`
       shorthand. */
    border-block-start: 3px solid var(--cp-color-accent);
    border-radius: var(--cp-radius-lg);
    padding: var(--cp-space-4);
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-2);
    transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

.cp-package-card:hover {
    box-shadow: var(--cp-shadow-lg);
    border-inline-color: var(--cp-color-primary-soft);
    border-block-end-color: var(--cp-color-primary-soft);
    transform: translateY(-2px);
}

.cp-package-card h3 {
    margin: 0;
    font-size: 1.05rem;
}

.cp-package-card button {
    align-self: start;
    background: var(--cp-gradient-brand);
    color: var(--cp-color-primary-text-on);
}

/* ---- Dashboard home ---- */

.cp-dashboard-summary {
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    padding: var(--cp-space-4);
    background: var(--cp-color-surface);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--cp-space-3);
    margin-block-end: var(--cp-space-5);
}

.cp-dashboard-summary h3 {
    margin: 0 0 var(--cp-space-1);
    font-size: 1rem;
}

.cp-dashboard-links {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: var(--cp-space-3);
}

/* Redesigned 2026-07-30 (product feedback: font too large, no real hover
   color) - smaller text/icon and tighter padding so more tool cards fit
   per row without dominating the page, and a genuine background-color
   wash on hover (not just a border/shadow tweak) so the hover state
   reads as an obvious color change, not a subtle lift. Uses the accent
   teal - the same "informational/navigational" hue as .cp-btn-info,
   since these cards are exactly that: links to another tool, not a
   destructive or confirming action. */
.cp-dashboard-link-card {
    display: flex;
    align-items: center;
    gap: var(--cp-space-2);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    padding: var(--cp-space-3);
    background: var(--cp-color-surface);
    text-decoration: none;
    color: var(--cp-color-text);
    /* Product feedback 2026-08-02: "سایز فونتشان هنوز بزرگ است و به‌نظرم
       ضرورتی ندارد که bold باشند" - down from 0.85rem/600 to match the
       .cp-btn-sm convention already used platform-wide for this same kind
       of secondary/utility action. */
    font-size: 0.8rem;
    font-weight: 500;
    /* position+overflow set up the hover accent bar (::before) below -
       this card has no absolutely-positioned children of its own, so
       clipping to the rounded corner here is safe. */
    position: relative;
    overflow: hidden;
    transition: background-color 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
}

/* Reading-start accent bar, hidden until hover (design pass 2026-07-29) -
   a quieter way to add brand color to a whole grid of these tool cards
   than tinting every one permanently, which would look busy at the
   6-8-card counts admin-console.php/organization-status.php actually use. */
.cp-dashboard-link-card::before {
    content: '';
    position: absolute;
    inset-inline-start: 0;
    inset-block: 0;
    width: 3px;
    background: var(--cp-gradient-brand-accent);
    opacity: 0;
    transition: opacity 0.15s ease;
}

.cp-dashboard-link-card:hover,
.cp-dashboard-link-card:focus-visible {
    /* The generic a:hover{text-decoration:underline} rule earlier in this
       file was leaking through here (nothing had overridden it for this
       specific property before) - design feedback (2026-07-31): these
       tool-link cards should never look like an underlined hyperlink. */
    text-decoration: none;
    background: var(--cp-color-accent-soft);
    box-shadow: var(--cp-shadow-lg);
    border-color: var(--cp-color-accent);
    color: var(--cp-color-accent);
    transform: translateY(-2px);
}

.cp-dashboard-link-card:hover::before,
.cp-dashboard-link-card:focus-visible::before {
    opacity: 1;
}

.cp-dashboard-link-card svg {
    width: 1.25rem;
    height: 1.25rem;
    flex-shrink: 0;
    color: var(--cp-color-primary);
    transition: color 0.15s ease;
}

.cp-dashboard-link-card:hover svg,
.cp-dashboard-link-card:focus-visible svg {
    color: var(--cp-color-accent);
}

/* Compact single-line stat cards (admin-analytics.php) - a separate
   modifier rather than changing .cp-dashboard-link-card itself, since
   that base class is shared with the icon+label tool-link cards on
   admin-console.php/organization-status.php/home.php/profile.php which
   need their stacked icon-above-text layout untouched. DOM order is
   caption then number (matches Persian reading order: title before the
   figure), tighter grid + padding fit more cards per row. */
.cp-dashboard-links--stat {
    /* auto-fit (not auto-fill): with a small fixed set of KPI cards, fitted
       tracks stretch to consume the whole row instead of leaving a trailing
       empty track reserved at the minmax floor - that empty track was
       eating width that should go to the (often long) Persian captions. */
    grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
    gap: var(--cp-space-2);
}

.cp-dashboard-link-card--stat {
    justify-content: space-between;
    padding: var(--cp-space-2) var(--cp-space-3);
    white-space: nowrap;
}

.cp-dashboard-link-card--stat .cp-dashboard-link-card__label {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 500;
    color: var(--cp-color-text-muted, var(--cp-color-text));
    overflow: hidden;
    text-overflow: ellipsis;
}

.cp-dashboard-link-card--stat .cp-number {
    flex-shrink: 0;
    font-weight: 700;
}

.cp-chat-widget__suggested-action {
    display: inline-block;
    text-decoration: none;
    font-weight: 600;
}

/* ---- Legal-acceptance blocking gate ---- */

.cp-legal-gate {
    position: fixed;
    inset: 0;
    background: rgba(15, 15, 20, 0.55);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
    padding: var(--cp-space-3);
}

.cp-legal-gate__panel {
    background: var(--cp-color-surface);
    border-radius: var(--cp-radius-lg);
    box-shadow: var(--cp-shadow-lg);
    padding: var(--cp-space-4);
    max-width: 32rem;
    width: 100%;
    max-height: 90vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-3);
}

.cp-legal-gate__document {
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    padding: var(--cp-space-3);
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-2);
}

.cp-legal-gate__content {
    max-height: 12rem;
    overflow-y: auto;
    font-size: 0.9rem;
    white-space: pre-wrap;
}

/* ---- Announcement banner ---- */

.cp-announcement-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--cp-space-2);
    padding: var(--cp-space-3);
    border-radius: var(--cp-radius);
    margin-block-end: var(--cp-space-3);
}

.cp-announcement-banner--info {
    background: var(--cp-color-primary-soft);
}

.cp-announcement-banner--warning {
    background: var(--cp-color-warning-soft);
}

.cp-announcement-banner--critical {
    background: var(--cp-color-error-soft);
}

.cp-announcement-banner__dismiss {
    background: transparent;
    border: none;
    cursor: pointer;
    font-size: 1.1rem;
    min-width: var(--cp-touch-target);
}

/* ---- Reusable progress + cancel modal ---- */

.cp-progress-modal {
    position: fixed;
    inset: 0;
    background: rgba(15, 15, 20, 0.5);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2100;
    padding: var(--cp-space-3);
}

.cp-progress-modal__panel {
    background: var(--cp-color-surface);
    border-radius: var(--cp-radius-lg);
    box-shadow: var(--cp-shadow-lg);
    padding: var(--cp-space-4);
    max-width: 26rem;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-3);
}

/* Wider variant for a modal hosting a report-table (search+stats+table+
   pagination needs more than the default confirm-dialog width). */
.cp-progress-modal__panel--wide {
    max-width: 50rem;
    max-height: 85vh;
    overflow-y: auto;
}

.cp-progress-modal__bar-track {
    background: var(--cp-color-border);
    border-radius: 999px;
    overflow: hidden;
    height: 0.6rem;
}

.cp-progress-modal__bar-fill {
    background: var(--cp-color-primary);
    height: 100%;
    transition: width 0.3s ease;
}

.cp-progress-modal__status {
    font-size: 0.9rem;
    color: var(--cp-color-text-muted);
}

.cp-progress-modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--cp-space-2);
}

.cp-progress-modal__spinner {
    width: 2rem;
    height: 2rem;
    margin: var(--cp-space-3) auto;
    border: 3px solid var(--cp-color-primary-soft);
    border-inline-start-color: var(--cp-color-primary);
    border-radius: 50%;
    animation: cp-spin 0.8s linear infinite;
}

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

.cp-form-hint {
    font-size: 0.85rem;
    color: var(--cp-color-text-muted);
    margin: 0;
}

.cp-checkbox-row {
    display: flex;
    align-items: center;
    gap: var(--cp-space-2);
    padding: var(--cp-space-2) 0;
    cursor: pointer;
}

/* ---- Toggle switch (active/inactive) - Pirang-style status control on
   the admin console's user list and an organization's member list. A
   plain <input type="checkbox"> underneath for native keyboard/form
   semantics - only the visual is custom. Use as:
   <label class="cp-toggle"><input type="checkbox">
     <span class="cp-toggle__track"><span class="cp-toggle__thumb"></span></span>
   </label> */
.cp-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    /* Larger than the visible track (34x20) so the actual tap target
       still clears this stylesheet's >=44px touch-target floor on mobile. */
    width: 2.75rem;
    height: var(--cp-touch-target);
    justify-content: center;
    /* QA report (2026-08-05): a toggle sitting in a table cell next to
       action buttons (organization-status.php's member row) looked
       vertically offset from them - same root cause as .cp-number's fix
       above: inline-flex's default vertical-align is 'baseline', not the
       parent <td>'s own vertical-align:middle. */
    vertical-align: middle;
}

.cp-toggle input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
}

.cp-toggle__track {
    display: inline-block;
    width: 2.4rem;
    height: 1.3rem;
    border-radius: 999px;
    background: var(--cp-color-border);
    transition: background-color 0.15s ease;
    flex-shrink: 0;
}

.cp-toggle__thumb {
    display: block;
    width: 1rem;
    height: 1rem;
    margin: 0.15rem;
    border-radius: 50%;
    background: var(--cp-color-surface);
    box-shadow: var(--cp-shadow-sm);
    transition: transform 0.15s ease;
    /* Logical inset-inline-start (not left/margin-inline-start alone)
       flips correctly under [dir="rtl"] without a separate rule. */
    inset-inline-start: 0;
    position: relative;
}

.cp-toggle input:checked ~ .cp-toggle__track {
    background: var(--cp-color-success);
}

.cp-toggle input:checked ~ .cp-toggle__track .cp-toggle__thumb {
    transform: translateX(1.1rem);
}

[dir="rtl"] .cp-toggle input:checked ~ .cp-toggle__track .cp-toggle__thumb {
    transform: translateX(-1.1rem);
}

.cp-toggle input:disabled ~ .cp-toggle__track {
    opacity: 0.5;
    cursor: not-allowed;
}

.cp-toggle input:focus-visible ~ .cp-toggle__track {
    box-shadow: 0 0 0 3px var(--cp-color-primary-soft);
}

/* Small muted metadata line under a status control - "who changed it and
   when" (design doc: admin/manager status changes are audited). */
.cp-status-meta {
    display: block;
    font-size: 0.75rem;
    color: var(--cp-color-text-muted);
    margin-block-start: var(--cp-space-1);
    white-space: nowrap;
}

/* ---- App header / nav (logged-in pages) ---- */

#cp-app-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cp-space-3);
    /* Full-bleed: body has its own horizontal padding (for .cp-container/
       #cp-app-footer's gutter), so this counteracts it with a matching
       negative margin at each breakpoint instead of being centered/capped
       like the content below it - module spec feedback: the header must
       span the entire viewport width, not just match the card's width. */
    width: calc(100% + 2 * var(--cp-space-3));
    margin-inline: calc(-1 * var(--cp-space-3));
    margin-block-end: var(--cp-space-2);
    padding: var(--cp-space-2) var(--cp-space-4);
    background: var(--cp-color-surface);
    border-block-end: 1px solid var(--cp-color-border);
    box-shadow: var(--cp-shadow-sm);
    /* position:sticky already establishes a positioning context for the
       ::after brand-underline below, no extra `position: relative` needed. */
    position: sticky;
    top: 0;
    z-index: 500;
}

/* Brand-colored underline (design pass 2026-07-29) - the nav previously
   had zero color of its own (plain white bar, grey 1px border), which
   read as generic/unbranded for the one element present on every single
   page. A thin two-hue gradient strip is a low-risk way to add identity
   without touching the nav's layout/height/sticky behavior at all. */
#cp-app-nav::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: -1px;
    height: 3px;
    background: var(--cp-gradient-brand-accent);
}

@media (min-width: 48rem) {
    #cp-app-nav {
        width: calc(100% + 2 * var(--cp-space-4));
        margin-inline: calc(-1 * var(--cp-space-4));
    }
}

.cp-app-nav__brand {
    display: flex;
    align-items: center;
    gap: var(--cp-space-2);
    font-weight: 700;
    color: var(--cp-color-text);
    text-decoration: none;
    margin-inline-end: var(--cp-space-2);
}

.cp-app-nav__brand:hover {
    text-decoration: none;
}

.cp-app-nav__brand-mark {
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--cp-radius-sm);
    background: var(--cp-gradient-brand);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 0.8rem;
    flex-shrink: 0;
}

/* Main app-specific sections (currently just "Organization") - kept as
   plain text links on the reading-start side, next to the brand. */
.cp-app-nav__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cp-space-1);
}

.cp-app-nav__links a {
    text-decoration: none;
    font-weight: 500;
    font-size: 0.9rem;
    color: var(--cp-color-text-muted);
    padding: var(--cp-space-1) var(--cp-space-2);
    border-radius: var(--cp-radius-sm);
    transition: background-color 0.15s ease, color 0.15s ease;
}

.cp-app-nav__links a:hover {
    background: var(--cp-color-primary-soft);
    color: var(--cp-color-primary);
    text-decoration: none;
}

/* Account actions (profile/billing/sessions/logout) - small round icon
   buttons pinned to the reading-end side (visually the left edge on an
   RTL page), tooltip-labeled via title/aria-label instead of visible
   text, so the main nav stays uncluttered - module spec feedback,
   modeled after the user's own Pirang product. */
/* Greeting + icons pinned together at the reading-end side (visually
   top-left in RTL) - the auto margin moved here from .cp-app-nav__icons
   alone so a personalized greeting can sit just before the icons without
   splitting away from them. */
.cp-app-nav__end {
    display: flex;
    align-items: center;
    gap: var(--cp-space-3);
    margin-inline-start: auto;
}

.cp-app-nav__greeting {
    font-size: 0.7rem;
    font-weight: 400;
    color: var(--cp-color-text-muted);
    white-space: nowrap;
}

.cp-app-nav__icons {
    display: flex;
    align-items: center;
    gap: var(--cp-space-1);
}

.cp-app-nav__icons a,
.cp-app-nav__icons button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    min-height: auto;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 50%;
    background-color: transparent;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 1.25rem 1.25rem;
    color: var(--cp-color-text-muted);
    font-size: 0;
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.cp-app-nav__icons a:hover,
.cp-app-nav__icons button:hover {
    background-color: var(--cp-color-primary-soft);
    color: var(--cp-color-primary);
    box-shadow: none;
    text-decoration: none;
}

.cp-app-nav__icons svg {
    width: 1.25rem;
    height: 1.25rem;
    flex-shrink: 0;
}

/* Pirang's own two actual icon assets (no baked-in text) - two-tone PNG
   swap, self-hosted under frontend/images/. Every OTHER icon is a plain
   inline SVG (frontend/js/icons.js) using stroke="currentColor" instead,
   so hover/dark-mode recoloring is one CSS `color` change - see
   .cp-app-nav__icons a/button above. That's also why "costs.png" (the
   Pirang money-bag icon) isn't used for billing: it has the word "ریال"
   drawn directly into the image, which stays Persian even when the UI
   language is switched to English/Arabic. */
.cp-icon-profile { background-image: url('../images/menuicons/account-silver.png'); }
.cp-icon-profile:hover { background-image: url('../images/menuicons/account.png'); }

.cp-icon-logout { background-image: url('../images/menuicons/logout-silver.png'); }
.cp-icon-logout:hover { background-image: url('../images/menuicons/logout.png'); }

.cp-app-nav__icons .cp-app-nav__logout-all:hover {
    background-color: var(--cp-color-error-soft);
    color: var(--cp-color-error);
}

/* Used on .cp-dashboard-link-card (profile.php's logout-all button, see
   its own comment there) - a red-tinted variant of the standard card, same
   specificity/source-order relationship as before this class stopped also
   being paired with the now-removed .cp-icon-text-btn. */
.cp-app-nav__logout-all {
    background: transparent;
    color: var(--cp-color-error);
    border: 1px solid color-mix(in srgb, var(--cp-color-error) 35%, transparent);
}

.cp-app-nav__logout-all:hover {
    background: var(--cp-color-error-soft);
}

/* ---- Password strength indicator ---- */

.cp-password-strength {
    height: 0.4rem;
    border-radius: 999px;
    background: var(--cp-color-border);
    overflow: hidden;
}

.cp-password-strength__fill {
    height: 100%;
    width: 0%;
    transition: width 0.2s ease, background 0.2s ease;
}

/* ---- Waiting-for-approval / notice screens ---- */

/* ---- Generic bordered section wrapper - used anywhere a page groups
   several blocks of content (org info, member management, etc.) that
   were previously bare text sitting directly on the page background. ---- */

.cp-section {
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    background: var(--cp-color-surface);
    padding: var(--cp-space-4);
    margin-block-end: var(--cp-space-4);
}

.cp-section > h3:first-child {
    margin-block-start: 0;
}

.cp-info-list {
    display: flex;
    flex-direction: column;
}

.cp-info-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--cp-space-3);
    padding-block: var(--cp-space-2);
    border-block-end: 1px solid var(--cp-color-border);
}

.cp-info-row:last-child {
    border-block-end: none;
    padding-block-end: 0;
}

.cp-info-row__label {
    color: var(--cp-color-text-muted);
}

.cp-notice-screen {
    text-align: center;
    padding: var(--cp-space-4);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    background: var(--cp-color-bg);
    margin-block-end: var(--cp-space-3);
}

.cp-notice-screen--success {
    background: var(--cp-color-success-soft);
    border-color: color-mix(in srgb, var(--cp-color-success) 30%, transparent);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--cp-space-1) var(--cp-space-2);
}

/* ---- Persistent non-blocking notice banner (module spec item 6) ---- */

.cp-notice-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--cp-space-2);
    padding: var(--cp-space-2) var(--cp-space-4);
    background: var(--cp-color-warning-soft);
    border-bottom: 1px solid var(--cp-color-border);
}

/* ---- Directional icons: mirror automatically in RTL ---- */

.icon-directional {
    display: inline-block;
}

[dir="rtl"] .icon-directional {
    transform: scaleX(-1);
}

/* ---- Bidi-safe embedded numbers inside RTL text ---- */

.cp-number {
    unicode-bidi: isolate;
    direction: ltr;
    display: inline-block;
    /* QA report (2026-08-05): number cells in a table row (e.g. the
       promotions table's discount/redemption columns) sat visibly off
       from plain-text cells in the same row - inline-block's default
       vertical-align is 'baseline', a different axis than the parent
       <td>'s own vertical-align:middle (base.css's .cp-table td rule),
       so a number's baseline didn't line up with its row siblings. */
    vertical-align: middle;
}

/* ---- Bidi-safe wrapper for whole runs of un-translated Latin-script
   data (package name/description, etc.) shown inside an RTL page ---- */

.cp-ltr-text {
    unicode-bidi: isolate;
    direction: ltr;
    text-align: start;
}

/* ---- Floating chat widget (module 11a) ---- */

.cp-chat-widget {
    position: fixed;
    inset-block-end: var(--cp-space-4);
    inset-inline-end: var(--cp-space-4);
    z-index: 1000;
}

.cp-chat-widget__toggle {
    width: 3.25rem;
    height: 3.25rem;
    border-radius: 50%;
    border: none;
    background: var(--cp-color-primary);
    color: #fff;
    font-size: 1.25rem;
    cursor: pointer;
    box-shadow: var(--cp-shadow);
}

.cp-chat-widget__toggle:hover {
    background: var(--cp-color-primary-hover);
}

.cp-chat-widget__panel {
    position: absolute;
    inset-block-end: 3.75rem;
    inset-inline-end: 0;
    width: 20rem;
    max-width: calc(100vw - 2rem);
    background: var(--cp-color-surface);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius-lg);
    display: flex;
    flex-direction: column;
    box-shadow: var(--cp-shadow-lg);
    overflow: hidden;
}

.cp-chat-widget__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--cp-space-2) var(--cp-space-3);
    border-block-end: 1px solid var(--cp-color-border);
    font-weight: 600;
    font-size: 0.9rem;
}

.cp-chat-widget__close {
    min-height: auto;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: none;
    background: transparent;
    font-size: 1.2rem;
    line-height: 1;
    color: var(--cp-color-text-muted);
}

.cp-chat-widget__close:hover {
    background: var(--cp-color-primary-soft);
    color: var(--cp-color-primary);
    box-shadow: none;
}

.cp-chat-widget__log {
    max-height: 20rem;
    overflow-y: auto;
    padding: var(--cp-space-3);
    display: flex;
    flex-direction: column;
    gap: var(--cp-space-2);
}

.cp-chat-widget__message {
    padding: var(--cp-space-2) var(--cp-space-3);
    border-radius: var(--cp-radius);
    max-width: 85%;
    text-align: start;
    font-size: 0.9rem;
}

.cp-chat-widget__message--user {
    align-self: flex-end;
    background: var(--cp-color-primary);
    color: #fff;
}

.cp-chat-widget__message--assistant {
    align-self: flex-start;
    background: var(--cp-color-bg);
}

.cp-chat-widget__confirm {
    display: flex;
    gap: var(--cp-space-2);
}

.cp-chat-widget__form {
    display: flex;
    gap: var(--cp-space-2);
    padding: var(--cp-space-3);
    border-block-start: 1px solid var(--cp-color-border);
}

.cp-chat-widget__input {
    flex: 1;
    min-height: 2.5rem;
    padding: var(--cp-space-2);
    border: 1px solid var(--cp-color-border);
    border-radius: var(--cp-radius);
}

/* ---- Back-to-parent-menu link (frontend/bootstrap.php's cp_back_link())
   - shown at the top-left of every sub-page's <main> so a user can always
   return to the menu they came from without relying on the browser's own
   back button. Icon uses .icon-directional (defined above) so it mirrors
   correctly under [dir="rtl"] automatically. ---- */

/* Pinned to the physical LEFT corner in BOTH rtl and ltr forms (design
   feedback, 2026-07-31: top-left reads better for rtl forms too, not just
   ltr - text-align:left is a PHYSICAL keyword, unlike the logical
   start/end, so unlike flex-start it does NOT flip under [dir="rtl"]). */
.cp-back-link-top {
    text-align: left;
    margin-block-end: var(--cp-space-3);
}

/* A real button (design feedback: a plain text link read as too subtle/
   easy to miss) rather than bare underlined text - markup now carries
   both .cp-btn-sm (shared bordered-pill sizing/look) and .cp-back-link
   (this class supplies only what's actually different: icon gap, bolder
   label, and a slightly richer hover than the plain a.cp-btn-sm one). */
.cp-back-link {
    gap: var(--cp-space-1);
    font-weight: 600;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.cp-back-link:hover {
    border-color: var(--cp-color-primary-soft);
    color: var(--cp-color-primary);
    box-shadow: var(--cp-shadow-sm);
}

.cp-back-link svg {
    width: 0.9rem;
    height: 0.9rem;
    flex-shrink: 0;
}

/* ---- App-wide footer (version + copyright/admin contact) ---- */

#cp-app-footer {
    max-width: 40rem;
    width: 100%;
    margin-inline: auto;
    /* Pins to the bottom of the viewport on short pages (see body{}) while
       still just following normal content flow on tall ones - the auto
       margin only expands when there's slack to fill. */
    margin-block-start: auto;
    flex-shrink: 0;
    padding-block: var(--cp-space-3);
    text-align: center;
}

.cp-app-footer__line {
    margin: 0;
    font-size: 0.8rem;
    color: var(--cp-color-text-muted);
}

.cp-app-footer__sep {
    display: inline-block;
    margin-inline: var(--cp-space-2);
    opacity: 0.6;
}

@media (prefers-color-scheme: dark) {
    :root {
        --cp-color-text: #e9e9ee;
        --cp-color-text-muted: #9a9ca6;
        --cp-color-bg: #0e0e12;
        --cp-color-surface: #1a1a21;
        /* #003366 itself is too dark to read against a near-black dark-mode
           background, so dark mode uses a lighter tint of the same navy
           hue instead of the raw brand color - same principle already
           applied to every other color pair in this block. */
        --cp-color-primary: #4d8bc9;
        --cp-color-primary-hover: #6fa3d8;
        /* Same lighter-tint principle applied to the new teal accent
           (design pass 2026-07-29) - #0e8a86 is readable enough on its
           own against the dark surface, unlike navy, but still gets a
           touch lighter for consistency with every other hover pair here. */
        --cp-color-accent: #14a8a3;
        --cp-color-accent-hover: #2ec2bd;
        --cp-color-border: #2c2c35;
        --cp-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
        --cp-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), 0 12px 28px -8px rgba(0, 0, 0, 0.5);
        --cp-shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.35), 0 24px 48px -12px rgba(0, 0, 0, 0.6);
    }
}
