/* cardly-base.css — Cardly Feel design system (Phase 1).
   Load FIRST, before the face stylesheet (cardly-public/op/admin.css): face rules
   intentionally win on conflicts so untouched pages don't regress.
   Tokens + cly-* primitives only. No page layout here. */

/* ---------- Display face ---------- */
@font-face {
    font-family: 'Fraunces';
    src: url('fonts/fraunces-var.woff2') format('woff2-variations');
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
    /* type */
    --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
    --text-xs: 12px;  --text-sm: 13px;  --text-md: 15px;
    --text-lg: 18px;  --text-xl: 24px;  --text-2xl: 32px;  --text-3xl: 44px;

    /* space (4px scale) */
    --space-1: 4px;  --space-2: 8px;   --space-3: 12px;  --space-4: 16px;
    --space-5: 24px; --space-6: 32px;  --space-7: 48px;  --space-8: 64px;

    /* radius + elevation */
    --radius-sm: 6px; --radius-md: 10px; --radius-lg: 16px; --radius-full: 999px;
    --shadow-1: 0 1px 3px rgba(15, 26, 42, .10);
    --shadow-2: 0 4px 14px rgba(15, 26, 42, .12);
    --shadow-3: 0 12px 36px rgba(15, 26, 42, .18);

    /* motion */
    --dur-fast: 120ms; --dur-base: 200ms; --dur-slow: 350ms;
    --ease-out: cubic-bezier(.22, .61, .36, 1);
    --ease-spring: cubic-bezier(.34, 1.4, .64, 1);

    /* brand-derived — static navy fallbacks first, color-mix() upgrades after */
    --brand: var(--brand-primary, #1f3a5f);
    --brand-on: #ffffff;                    /* recomputed by CardlyUI.applyBrandOn() */
    --brand-tint-1: #e9eef5;
    --brand-tint-2: #d3dcea;
    --brand-hover:  #18304f;

    /* neutrals */
    --surface: #ffffff; --surface-2: #f6f7f9;
    --ink: #17202b; --ink-2: #5b6672; --line: #e3e7ec;
    --ok: #15803d; --err: #b91c1c; --warn: #b45309;
}
@supports (color: color-mix(in srgb, red 50%, white)) {
    :root {
        --brand-tint-1: color-mix(in srgb, var(--brand) 9%, white);
        --brand-tint-2: color-mix(in srgb, var(--brand) 18%, white);
        --brand-hover:  color-mix(in srgb, var(--brand) 88%, black);
    }
}

/* Dark tokens — OP face only. Two triggers, same values:
   1) explicit choice: data-theme="dark" on <html> (header toggle, localStorage cardly.theme)
   2) OS preference: prefers-color-scheme dark, unless the user explicitly chose light.
   Buyer faces stay light-first (card.jsp owns its own independent dark stage).
   In dark, panels (--surface) are LIGHTER than the page (--surface-2). */
:root[data-theme="dark"][data-face="op"] {
    --surface: #1d242e; --surface-2: #12161c;
    --ink: #e7ebf0; --ink-2: #9aa5b1; --line: #2b3441;
    --brand-tint-1: #222b37; --brand-tint-2: #2b3644;
    --shadow-1: 0 1px 3px rgba(0, 0, 0, .5);
    --shadow-2: 0 4px 14px rgba(0, 0, 0, .5);
    --shadow-3: 0 12px 36px rgba(0, 0, 0, .6);
}
@media (prefers-color-scheme: dark) {
    :root[data-face="op"]:not([data-theme="light"]) {
        --surface: #1d242e; --surface-2: #12161c;
        --ink: #e7ebf0; --ink-2: #9aa5b1; --line: #2b3441;
        --brand-tint-1: #222b37; --brand-tint-2: #2b3644;
        --shadow-1: 0 1px 3px rgba(0, 0, 0, .5);
        --shadow-2: 0 4px 14px rgba(0, 0, 0, .5);
        --shadow-3: 0 12px 36px rgba(0, 0, 0, .6);
    }
}

/* ---------- Reduced motion: kill it all ---------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}

/* ---------- Shared text helpers ----------
   .muted was defined ONLY in cardly-op.css, which the PUBLIC pages do not load, so every
   <small class="muted"> on the storefront, the thanks page and the card page rendered as
   full-weight body copy — including the under-field help text the placeholder directive
   requires to be visibly secondary. Defining it here, in the file every face loads, is what
   makes the class mean the same thing on all three. cardly-op.css keeps its identical rule
   and simply wins by load order, with the same value. */
.muted { color: var(--ink-2); }

/* Under-field help text. This is the home of the directive that forbids a `placeholder` on a
   field that already has a <label>: the example, the unit and the "leave empty to ..." rule all
   live in a <div class="form-text"> UNDER the field, where they survive the first keystroke.
   font-size is part of the rule and not optional: .muted is colour ONLY, so a bare .muted help
   line renders at full body size and reads as a second label. The three PUBLIC pages (card,
   open, corporate) still repeat this declaration inline because they shipped before this class
   existed. Those copies were NOT equivalent until 2026-08-10: they hardcoded #6b7280 against this
   rule's var(--ink-2) = #5b6672 and, sitting in a page-local <style> after the linked stylesheet at
   equal specificity, they WON on the three highest-traffic buyer pages. They now use the token, so
   deleting them is finally the no-op the earlier version of this comment wrongly claimed it already
   was. Verify the computed colour before deleting anything on the strength of a comment. */
.form-text {
    display: block;
    color: var(--ink-2);
    font-size: 12px;
    font-weight: 400;
    line-height: 1.45;
    margin-top: 4px;
}

/* ---------- Gentle, additive button feel (face CSS keeps visual ownership) ---------- */
.btn { transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
.btn:active { transform: scale(.97); }
.btn.is-loading { position: relative; pointer-events: none; color: transparent !important; }
.btn.is-loading::after {
    content: ""; position: absolute; inset: 0; margin: auto;
    width: 18px; height: 18px; border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, .35); border-top-color: #fff;
    animation: cly-spin .7s linear infinite;
}
@keyframes cly-spin { to { transform: rotate(360deg); } }

/* ---------- Skeleton ---------- */
.cly-skel {
    display: block; height: 14px; border-radius: var(--radius-sm);
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--line) 50%, var(--surface-2) 75%);
    background-size: 200% 100%;
    animation: cly-shimmer 1.2s ease-in-out infinite;
    margin: var(--space-2) 0;
}
@keyframes cly-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ---------- Toasts ---------- */
.cly-toast-host {
    position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%); z-index: 10000;
    display: flex; flex-direction: column; gap: var(--space-2); align-items: center;
    pointer-events: none; width: min(92vw, 480px);
}
.cly-toast {
    pointer-events: auto; display: flex; align-items: center; gap: var(--space-3);
    background: var(--ink); color: #fff; font-family: var(--font-ui); font-size: var(--text-md);
    padding: 12px 18px; border-radius: var(--radius-md); box-shadow: var(--shadow-2);
    opacity: 0; transform: translateY(12px);
    transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-spring);
    max-width: 100%;
}
.cly-toast.show { opacity: 1; transform: translateY(0); }
.cly-toast.success { background: var(--ok); }
.cly-toast.error   { background: var(--err); }
.cly-toast-close {
    background: none; border: 0; color: inherit; font-size: 18px; line-height: 1;
    cursor: pointer; padding: 0 0 0 4px; opacity: .8;
}
.cly-toast-close:hover { opacity: 1; }

/* ---------- Modals ---------- */
.cly-modal-backdrop {
    position: fixed; inset: 0; background: rgba(10, 16, 24, .55); z-index: 10001;
    display: flex; align-items: center; justify-content: center; padding: var(--space-4);
    opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.cly-modal-backdrop.show { opacity: 1; }
.cly-modal {
    background: var(--surface); color: var(--ink); font-family: var(--font-ui);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-3);
    width: min(92vw, 420px); padding: var(--space-5);
    transform: scale(.96); transition: transform var(--dur-base) var(--ease-spring);
}
.cly-modal-backdrop.show .cly-modal { transform: scale(1); }
.cly-modal-title { margin: 0 0 var(--space-2); font-size: var(--text-lg); font-weight: 600; }
.cly-modal-message { margin: 0 0 var(--space-4); color: var(--ink-2); font-size: var(--text-md); white-space: pre-line; }
.cly-modal-input {
    width: 100%; box-sizing: border-box; font-size: var(--text-md); font-family: var(--font-ui);
    padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm);
    margin-bottom: var(--space-4); background: var(--surface); color: var(--ink);
}
.cly-modal-actions { display: flex; gap: var(--space-3); justify-content: flex-end; }

/* The copy-to-clipboard row inside a modal (CardlyUI.alert with opts.copyText). Was three inline
   style attributes inside an innerHTML template until 2026-09-07; that is a style-src-attr
   violation under the strict CSP, so it is a class now. */
.cly-copy-row { display: flex; gap: var(--space-2); margin-bottom: var(--space-4); }
.cly-copy-row .cly-modal-input { margin-bottom: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.cly-copy-row .cly-copy-btn { flex: 0 0 auto; }

/* ---------- Popover (speaker volume etc.) ---------- */
.cly-popover {
    position: absolute; z-index: 10002; background: var(--surface); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--radius-md); box-shadow: var(--shadow-2);
    padding: var(--space-4); font-family: var(--font-ui); font-size: var(--text-sm);
    min-width: 200px;
}
.cly-popover label { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.cly-popover input[type="range"] { width: 100%; accent-color: var(--brand); }

/* ================================================================================================
   Utilities — the classes that replaced the inline `style="…"` attributes (CSP phase 2, 2026-09-07)
   ================================================================================================

   Why these exist at all. A strict `style-src 'self'` refuses two things and permits a third, and
   the difference decides how every one of the ~500 former inline styles had to be rewritten. Measured
   in Chrome against the Report-Only header on cardly-stage, 2026-09-07:

     - `el.style.width = '42px'`            -> NO violation. The CSSOM is not covered by style-src.
     - `el.setAttribute('style', '...')`    -> style-src-attr violation.
     - `el.innerHTML = '<b style="...">'`   -> style-src-attr violation.
     - `document.head.appendChild(style)`   -> style-src-elem violation.

   So: a STATIC style belongs in a class, here or in the page's own stylesheet; a DYNAMIC value
   (a bar width, a computed colour) is assigned through `el.style.prop` AFTER the node is in the
   document, never interpolated into markup. Do not "simplify" such an assignment back into a
   template string: it works today only because the applied policy still carries 'unsafe-inline'
   on style-src, and the whole point of this section is to be able to drop it.

   Naming: `cly-u-` (u for utility) so these never collide with the face stylesheets, which keep
   visual ownership of everything that is not pure geometry. Kept deliberately small — a page-shaped
   rule belongs in that page's own file under RESOURCES/pages/, not here. */

/* alignment — 39 sites, almost all numeric table columns */
.cly-u-right  { text-align: right; }
.cly-u-left   { text-align: left; }
.cly-u-center { text-align: center; }

/* visibility. `hidden` over `display:none` where the element is toggled: it is one attribute
   instead of one class, and it carries the meaning to assistive technology for free. This class
   stays for the cases that toggle a class rather than the attribute. */
.cly-u-hidden { display: none !important; }

/* vertical rhythm, on the 4px token scale */
.cly-u-mt-0  { margin-top: 0; }
.cly-u-mt-1  { margin-top: var(--space-1); }
.cly-u-mt-2  { margin-top: var(--space-2); }
.cly-u-mt-3  { margin-top: var(--space-3); }
.cly-u-mt-4  { margin-top: var(--space-4); }
.cly-u-mt-5  { margin-top: var(--space-5); }
.cly-u-mt-6  { margin-top: var(--space-6); }
.cly-u-mb-0  { margin-bottom: 0; }
.cly-u-mb-2  { margin-bottom: var(--space-2); }
.cly-u-mb-3  { margin-bottom: var(--space-3); }
.cly-u-mb-4  { margin-bottom: var(--space-4); }
.cly-u-m-0   { margin: 0; }

/* secondary copy — 18 sites of `color:#6b7280; font-size:12px`, which is .form-text without the
   block display. #6b7280 was a hardcoded near-miss of --ink-2 (#5b6672); the token wins here. */
.cly-u-note  { color: var(--ink-2); font-size: var(--text-xs); }
.cly-u-note-sm { color: var(--ink-2); font-size: var(--text-sm); }

/* rows of controls */
.cly-u-row      { display: flex; align-items: center; gap: var(--space-2); }
.cly-u-row-wrap { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.cly-u-actions  { display: flex; gap: var(--space-2); justify-content: flex-end; }

/* pointer affordance (directive C1) */
.cly-u-click { cursor: pointer; }

/* the generic text field that 13 sites drew by hand */
.cly-u-field {
    width: 100%; box-sizing: border-box; padding: var(--space-2);
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    font-family: var(--font-ui); font-size: var(--text-sm);
    background: var(--surface); color: var(--ink);
}

/* plain data table + its cells, the shape repeated across the operator reports */
.cly-u-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.cly-u-table th, .cly-u-table td { padding: 10px var(--space-3); border-bottom: 1px solid var(--line); }
.cly-u-table th { text-align: left; }

/* empty-state block */
.cly-u-empty { text-align: center; padding: 60px var(--space-4); color: var(--ink-3, var(--ink-2)); }

/* error / warning panel */
.cly-u-alert {
    background: #fee2e2; border-left: 4px solid #991b1b; color: #7f1d1d;
    padding: 14px var(--space-4); border-radius: var(--radius-sm);
}

/* a bar whose length is data. The length itself is set with el.style.width in JS, because a value
   that comes from data cannot be a class and must not be interpolated into markup. */
.cly-u-bar-track { background: var(--surface-2, #f3f4f6); border-radius: var(--radius-full); overflow: hidden; }
.cly-u-bar-fill  { display: block; height: 100%; background: var(--brand); }

/* Wide content (a table, a diagram) scrolls inside its own box instead of making the page scroll
   sideways. Added with the modules tab, whose history table has six columns. */
.cly-u-scroll-x
{
    overflow-x: auto;
    max-width: 100%;
}
