/*
=====================================================
Etebarit - by Gisoo Web Solutions
------------------------------------------------------
Gisoo.net
------------------------------------------------------
Copyright (c) 2026 Gisoo Web Solutions
=====================================================
This code is protected by copyright
=====================================================
File: app.css
------------------------------------------------------
Use: Global stylesheet. Declares the self-hosted
     IranYekan faces, the brand palette and the RTL
     base layer shared by the site and the admin panel.
=====================================================
*/

@import 'tailwindcss';

@source '../views/**/*.blade.php';
@source '../js/**/*.js';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';

/* Turn off double-tap-to-zoom site-wide (pinch is handled in app.js for iOS).
   Leaflet sets its own touch-action on the map, so the address map still zooms. */
@layer base {
    html {
        touch-action: manipulation;
    }

    /* Buttons are never text: stop long-press from selecting their label or
       popping the iOS callout menu (annoying on the numeric keypads). */
    button,
    [role='button'] {
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        user-select: none;
    }
}

/* -----------------------------------------------------------------------
 | IranYekan -- served from this project only. Never load a webfont from an
 | external host: Iran's international connectivity is unreliable and the UI
 | must render identically when it is down.
 |
 | woff2 is the primary format; woff covers older Android browsers. Paths are
 | relative so Vite fingerprints each file for cache busting.
 ----------------------------------------------------------------------- */

@font-face {
    font-family: 'IranYekan';
    font-style: normal;
    font-weight: 100;
    font-display: swap;
    src:
        url('../fonts/woff2/iranyekanwebthin.woff2') format('woff2'),
        url('../fonts/woff/iranyekanwebthin.woff') format('woff');
}

@font-face {
    font-family: 'IranYekan';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src:
        url('../fonts/woff2/iranyekanweblight.woff2') format('woff2'),
        url('../fonts/woff/iranyekanweblight.woff') format('woff');
}

@font-face {
    font-family: 'IranYekan';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src:
        url('../fonts/woff2/iranyekanwebregular.woff2') format('woff2'),
        url('../fonts/woff/iranyekanwebregular.woff') format('woff');
}

@font-face {
    font-family: 'IranYekan';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src:
        url('../fonts/woff2/iranyekanwebmedium.woff2') format('woff2'),
        url('../fonts/woff/iranyekanwebmedium.woff') format('woff');
}

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

@font-face {
    font-family: 'IranYekan';
    font-style: normal;
    font-weight: 800;
    font-display: swap;
    src:
        url('../fonts/woff2/iranyekanwebextrabold.woff2') format('woff2'),
        url('../fonts/woff/iranyekanwebextrabold.woff') format('woff');
}

@font-face {
    font-family: 'IranYekan';
    font-style: normal;
    font-weight: 900;
    font-display: swap;
    src:
        url('../fonts/woff2/iranyekanwebblack.woff2') format('woff2'),
        url('../fonts/woff/iranyekanwebblack.woff') format('woff');
}

@font-face {
    font-family: 'IranYekan';
    font-style: normal;
    font-weight: 950;
    font-display: swap;
    src:
        url('../fonts/woff2/iranyekanwebextrablack.woff2') format('woff2'),
        url('../fonts/woff/iranyekanwebextrablack.woff') format('woff');
}

/* -----------------------------------------------------------------------
 | Design tokens. `primary` is the blue-violet brand colour (#563bb1) and
 | `secondary` the Iranian red (#e80e23). Both are used sparingly; the UI is
 | otherwise neutral, simple and minimal.
 ----------------------------------------------------------------------- */

@theme {
    --font-sans: 'IranYekan', ui-sans-serif, system-ui, sans-serif;

    /* Neutral palette lifted from the approved panel design (قالب پنل). */
    --color-page: #f6f6fa;
    --color-line: #ececf2;
    --color-ink: #1c1830;
    --color-ink-soft: #5b5872;
    --color-ink-muted: #7b7890;
    --color-ink-faint: #9491a6;

    --color-primary-50: #f2effb;
    --color-primary-100: #e5def6;
    --color-primary-200: #cbbdee;
    --color-primary-300: #b09ce5;
    --color-primary-400: #967bdd;
    --color-primary-500: #7c5ad4;
    --color-primary-600: #563bb1;
    --color-primary-700: #46308f;
    --color-primary-800: #35246c;
    --color-primary-900: #251949;
    --color-primary-950: #150e29;

    --color-secondary-50: #fdecee;
    --color-secondary-100: #fbd3d7;
    --color-secondary-200: #f6a7af;
    --color-secondary-300: #f27b87;
    --color-secondary-400: #ee4f5f;
    --color-secondary-500: #e80e23;
    --color-secondary-600: #c40c1d;
    --color-secondary-700: #930917;
    --color-secondary-800: #620610;
    --color-secondary-900: #310308;
    --color-secondary-950: #190204;
}

/* -----------------------------------------------------------------------
 | Shared paginator accent. Blue-violet everywhere by default; the merchant
 | panel adds `.panel-accent-merchant` on <body> to turn it Iranian-red.
 ----------------------------------------------------------------------- */

:root {
    --pager-accent: #563bb1;
}

.panel-accent-merchant {
    --pager-accent: #e80e23;
}

/* -----------------------------------------------------------------------
 | Auth pages -- brand theme flips between user (violet) and merchant (red)
 | by swapping CSS variables, exactly as in the approved auth design.
 ----------------------------------------------------------------------- */

.auth-theme {
    --brand: var(--color-primary-600);
    --brand-dark: var(--color-primary-700);
    --brand-soft: var(--color-primary-50);
    --brand-g1: #5e42be;
    --brand-g2: #3f2a8f;
    --brand-shadow: rgba(86, 59, 177, 0.28);
    --brand-ring: rgba(86, 59, 177, 0.12);
    --brand-border: #e4ddf7;
}

.auth-theme[data-theme='merchant'] {
    --brand: var(--color-secondary-500);
    --brand-dark: var(--color-secondary-600);
    --brand-soft: var(--color-secondary-50);
    --brand-g1: #e80e23;
    --brand-g2: #b3242f;
    --brand-shadow: rgba(232, 14, 35, 0.28);
    --brand-ring: rgba(232, 14, 35, 0.12);
    --brand-border: #f6ccd1;
}

@keyframes auth-drift {
    0%,
    100% {
        transform: translate(0, 0) scale(1);
    }
    50% {
        transform: translate(16px, -12px) scale(1.06);
    }
}

@keyframes auth-fade-up {
    from {
        opacity: 0;
        transform: translateY(14px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes auth-blink {
    0%,
    49% {
        opacity: 1;
    }
    50%,
    100% {
        opacity: 0;
    }
}

/* -----------------------------------------------------------------------
 | Base layer -- right-to-left everywhere.
 ----------------------------------------------------------------------- */

@layer base {
    html {
        direction: rtl;
        /* Reserve the scrollbar so locking scroll for a modal cannot shift
           the layout sideways. */
        scrollbar-gutter: stable;
    }

    /* Tailwind v4's preflight leaves buttons with the default arrow cursor.
       Every enabled interactive element in this project shows the pointer,
       and disabled ones say so -- globally, not per component. */
    button:not(:disabled),
    [role='button']:not(:disabled),
    input[type='checkbox']:not(:disabled),
    input[type='radio']:not(:disabled),
    input[type='file']:not(:disabled),
    select:not(:disabled),
    summary,
    label[for] {
        cursor: pointer;
    }

    button:disabled {
        cursor: not-allowed;
    }

    body {
        font-family: var(--font-sans);
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    /* Alpine sets x-cloak until it has booted. Hiding those nodes up front is
       what keeps the first paint clean -- no flash of unstyled content. */
    [x-cloak] {
        display: none !important;
    }

    /* Scroll-reveal: elements start hidden ONLY while the reveal script owns
       the page (`.has-reveal` on <html>, set by an inline head script with a
       failsafe timeout). This keeps the pre-JS paint from flashing the content
       before it animates in, yet never leaves anything hidden if JS never runs.
       Motion-averse visitors keep everything visible. */
    @media (prefers-reduced-motion: no-preference) {
        .has-reveal [data-reveal],
        .has-reveal [data-reveal-hero] {
            opacity: 0;
        }
    }

    /* 3D hero: when the head script has committed to WebGL, hold the static
       fallback card and the flat CSS blobs invisible from the first paint so
       the old design never flashes before the canvases fade in. The classes
       are cleared (head failsafe / JS catch) if the canvases never appear. */
    /* 3D hero (preview page): the WebGL field owns the background and the cycle
       owns the card, so keep the flat CSS blobs, the static fallback card and
       the 2D diagram hidden from first paint. The classes clear (head failsafe /
       load .catch) if a canvas never appears, restoring these fallbacks. */
    .hero-3d-blobs [data-blob] {
        opacity: 0;
    }
    .hero-3d-cycle [data-hero-card-fallback],
    .hero-3d-cycle [data-hero-diagram] {
        opacity: 0 !important;
    }
}

/* -----------------------------------------------------------------------
 | Home pinned-scroll hero -- keyframes and the mobile/reduced-motion
 | fallback for the scroll-scrubbed BNPL flow. The scrubbing itself is driven
 | in JS (initHomeHero); these rules cover the ambient float animations and
 | collapse the 420vh pinned stage into a normal static block on small
 | screens or when motion is unwelcome.
 ----------------------------------------------------------------------- */
@keyframes hero-pulse-dot {
    0%, 100% { box-shadow: 0 0 0 0 rgba(232, 14, 35, .4); }
    70% { box-shadow: 0 0 0 11px rgba(232, 14, 35, 0); }
}
@keyframes hero-bob {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(7px); }
}
@keyframes hero-drift {
    0%, 100% { transform: translate(0, 0) scale(1); }
    50% { transform: translate(14px, -10px) scale(1.06); }
}
@keyframes hero-hub-glow {
    0%, 100% { box-shadow: 0 20px 50px rgba(86, 59, 177, .38); }
    50% { box-shadow: 0 22px 64px rgba(86, 59, 177, .55); }
}

.hero-pulse-dot { animation: hero-pulse-dot 2s infinite; }
.hero-bob { animation: hero-bob 4s ease-in-out infinite; }
.hero-bob-slow { animation: hero-bob 5s ease-in-out infinite .5s; }
.hero-drift { animation: hero-drift 13s ease-in-out infinite; }
.hero-drift-2 { animation: hero-drift 17s ease-in-out infinite; }
.hero-drift-3 { animation: hero-drift 11s ease-in-out infinite; }
.hero-drift-4 { animation: hero-drift 15s ease-in-out infinite; }
.hero-drift-badge { animation: hero-drift 9s ease-in-out infinite; }
.hero-drift-bag { animation: hero-drift 11s ease-in-out infinite; }
.hero-hub-glow { animation: hero-hub-glow 4s ease-in-out infinite; }

/* Store-logo marquee (the "shops that work with Etebarit" strip). The track
   holds two identical sets and slides by half its width for a seamless loop. */
@keyframes logo-marquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}
.logo-marquee { animation: logo-marquee 32s linear infinite; }
.logo-marquee-mask {
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

/* Desktop: pull the pinned stage up under the sticky 72px header so the 100vh
   stage fills the real viewport -- otherwise the stage overflows the fold and
   the scroll cue drops out of first view. The translucent header floats over
   the hero, matching the design. */
@media (min-width: 860px) {
    [data-pin-hero] { margin-top: -72px; }
    [data-hero-intro] { padding-top: 72px; }
}

/* Small screens: no pin/scrub. The intro stands on its own, the diagram and
   the scroll cue are dropped. Matches initHomeHero's 860px breakpoint. */
@media (max-width: 859px) {
    [data-pin-hero] { height: auto !important; }
    [data-pin-stage] { position: static !important; height: auto !important; min-height: auto !important; overflow: visible !important; }
    /* Relative (not static) so its z-index still wins -- the intro must sit
       above any decorative sibling, or nothing in the hero is clickable. */
    [data-hero-intro] { position: relative !important; inset: auto !important; z-index: 20 !important; padding: 40px 20px 52px !important; }
    [data-hero-diagram], [data-scroll-cue] { display: none !important; }
    /* The decorative hero blobs sit off the edges; with the stage no longer
       clipping them on mobile they would push the page sideways, so drop them. */
    [data-blob] { display: none !important; }
    /* Belt-and-braces: nothing may cause a horizontal scroll on phones. Safe
       here because the pinned/sticky hero is collapsed at this breakpoint. */
    body { overflow-x: hidden; }
}

/* The hero's scroll-scrubbed diagram and cue are pure decoration -- they must
   never intercept clicks or text selection over the intro. */
[data-hero-diagram], [data-scroll-cue] { pointer-events: none; }

/* Reduced motion: freeze the ambient float loops and collapse the pinned
   stage into a static intro (the JS scrub is skipped too), so there is never
   an empty 420vh of scrolling. */
@media (prefers-reduced-motion: reduce) {
    .hero-pulse-dot, .hero-bob, .hero-bob-slow, .hero-drift, .hero-drift-2,
    .hero-drift-3, .hero-drift-4, .hero-drift-badge, .hero-drift-bag,
    .hero-hub-glow, .logo-marquee { animation: none !important; }
    [data-pin-hero] { height: auto !important; }
    [data-pin-stage] { position: static !important; height: auto !important; min-height: auto !important; overflow: visible !important; }
    [data-hero-intro] { position: relative !important; inset: auto !important; z-index: 20 !important; padding: 40px 20px 52px !important; }
    [data-hero-diagram], [data-scroll-cue] { display: none !important; }
}

/* -----------------------------------------------------------------------
 | Brand range slider -- the amount picker in the credit-request wizard.
 | Styled by hand because the native track/thumb can't be reached from
 | utility classes, and the look must match the approved design exactly.
 ----------------------------------------------------------------------- */

.range-brand {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 6px;
    border-radius: 6px;
    background: #e6e4ee;
    outline: none;
}

.range-brand::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--color-primary-600);
    border: 3px solid #fff;
    box-shadow: 0 3px 8px rgba(86, 59, 177, 0.4);
    cursor: pointer;
}

.range-brand::-moz-range-thumb {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--color-primary-600);
    border: 3px solid #fff;
    box-shadow: 0 3px 8px rgba(86, 59, 177, 0.4);
    cursor: pointer;
}

/* -----------------------------------------------------------------------
 | Public payment page -- a standalone surface with its own day/night mode,
 | ported from the approved design (صفحه پرداخت). Colours live in variables
 | so the whole card flips theme without a single duplicated utility class.
 ----------------------------------------------------------------------- */

.pay-theme {
    --pay-bg: #eceaf3;
    --pay-card: #ffffff;
    --pay-text: #1c1830;
    --pay-muted: #7b7890;
    --pay-border: #ececf2;
    --pay-input: #fbfbfd;
    --pay-purple-soft: #efeafb;
    --pay-green-soft: #e7f7ee;
    --pay-track: #eeedf3;
    --pay-tx: #faf9fd;
    --pay-chip: #fdeaec;
    --pay-tab: #f2f0fa;
    --pay-tab-on: #ffffff;
    --pay-shadow: 0 30px 70px rgba(20, 16, 40, 0.12);
    --pay-notes-bg: #fff7ea;
    --pay-notes-border: #f6e6c6;
    --pay-blob-1: rgba(122, 93, 224, 0.16);
    --pay-blob-2: rgba(86, 59, 177, 0.12);
    --pay-blob-3: rgba(232, 14, 35, 0.08);
}

.pay-theme[data-mode='dark'] {
    --pay-bg: #12101c;
    --pay-card: #1c1930;
    --pay-text: #f4f3fa;
    --pay-muted: #9a97ad;
    --pay-border: #2a2740;
    --pay-input: #26233a;
    --pay-purple-soft: #2a2440;
    --pay-green-soft: #1e3328;
    --pay-track: #2a2740;
    --pay-tx: #26233a;
    --pay-chip: #2a2233;
    --pay-tab: #26233a;
    --pay-tab-on: #3a3550;
    --pay-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
    --pay-notes-bg: rgba(217, 119, 6, 0.08);
    --pay-notes-border: rgba(217, 119, 6, 0.25);
}

/* The receipt tints the whole background with its outcome. */
.pay-theme[data-outcome='success'] {
    --pay-blob-1: rgba(22, 163, 74, 0.22);
    --pay-blob-2: rgba(34, 197, 94, 0.18);
    --pay-blob-3: rgba(22, 163, 74, 0.14);
}

.pay-theme[data-outcome='failed'] {
    --pay-blob-1: rgba(232, 14, 35, 0.22);
    --pay-blob-2: rgba(232, 14, 35, 0.16);
    --pay-blob-3: rgba(199, 11, 29, 0.14);
}

@keyframes pay-blob-float {
    0%,
    100% {
        transform: translate3d(0, 0, 0) rotate(0deg);
    }
    50% {
        transform: translate3d(16px, -14px, 40px) rotate(8deg);
    }
}

@keyframes pay-ring {
    from {
        stroke-dashoffset: 283;
    }
}

@keyframes pay-pop-in {
    0% {
        opacity: 0;
        transform: scale(0.6);
    }
    60% {
        transform: scale(1.08);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}

/* -----------------------------------------------------------------------
 | Printing a receipt. The page prints only the sheet that asked for it:
 | everything else is made invisible rather than removed, so the modal keeps
 | its layout instead of collapsing into the flow.
 ----------------------------------------------------------------------- */

.print-only {
    display: none;
}

@media print {
    body.is-printing * {
        visibility: hidden !important;
    }

    body.is-printing [data-print-area],
    body.is-printing [data-print-area] * {
        visibility: visible !important;
    }

    body.is-printing [data-print-hide],
    body.is-printing [data-print-hide] * {
        display: none !important;
    }

    body.is-printing [data-print-area] {
        position: absolute !important;
        inset: 0 0 auto 0;
        margin: 0 auto;
        max-width: 100% !important;
        max-height: none !important;
        overflow: visible !important;
        border-radius: 0 !important;
        box-shadow: none !important;
        /* Keep the brand header and the status colours on paper. */
        print-color-adjust: exact;
        -webkit-print-color-adjust: exact;
    }

    body.is-printing .print-only {
        display: block !important;
    }

    @page {
        margin: 14mm;
    }
}

/* ---------------------------------------------------------------------
 | Dialog stacking contract.
 |
 | Content dialogs (receipts, editors, image viewer, add-account, …) all
 | share ONE layer, and the single confirm/error dialog sits an order of
 | magnitude above it. Never hand-write a z-index on a dialog: raising one
 | modal past the confirm dialog is how a confirmation ends up invisible
 | behind the modal that raised it.
 --------------------------------------------------------------------- */
.z-modal {
    z-index: 100;
}

.z-dialog {
    z-index: 1000;
}

/* ---------------------------------------------------------------------
 | Leaflet map (physical-card delivery pin). Isolate the container so
 | Leaflet's internal z-indexes never climb over the confirm dialog or
 | the offline notice, and strip the default box off our SVG pin.
 --------------------------------------------------------------------- */
.etb-map {
    isolation: isolate;
}

.leaflet-div-icon.etb-pin {
    background: transparent;
    border: 0;
    filter: drop-shadow(0 4px 6px rgba(20, 16, 40, 0.3));
}
