/* ══════════════════════════════════════════════════════════════════════════
   رمز التحقق المعتمد — الخانات الأربع، والختم، والحكم. مشترك بين:

     منصة التطوع    vo/register/verify.blade.php
     الموارد البشرية  auth/recover/verify.blade.php  (تفعيل الحساب واستعادة كلمة المرور)

   نُقل كما هو من vo-register.css (فارس، 2026-10-01: «التصميم المعتمد في otp»)، والحركة
   في public/assets/js/vo-otp.js. الرموز أدناه هي قيم صفحة التسجيل نفسها، معرَّفة هنا
   كي يلبس الرمز الشكل نفسه في أي صفحة تحمّل هذا الملف.
   ══════════════════════════════════════════════════════════════════════════ */

.otp,
.otp-foot {
    --brand-600: #cf6028;
    --brand-500: #f18927;
    --brand-200: #ffd4af;
    --rg-line: #e8e0d7;
    --rg-ink: #242824;
    --rg-ink-700: #505650;
    --rg-ink-500: #7c837b;
    --rg-danger: #a53636;
    --rg-success: #27734e;
    --rg-success-bg: #e9f7ef;
}

.otp .fld-hp {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}


.otp {
    --otp-tile-w: clamp(3.1rem, 14vw, 3.9rem);
    --otp-tile-h: clamp(3.6rem, 16vw, 4.5rem);
    --otp-gap: clamp(.45rem, 2.2vw, .8rem);
    --otp-radius: .7rem;
}

/*
    المسرح: ارتفاعه ثابت.

    Four tiles in a row become four tiles in a square, and the card must not jump between the
    two — so the stage is as tall as the square needs and the row simply sits in the middle of
    it. `overflow: visible` because the fan rotates the tiles outside their own box.
*/
.otp-stage {
    position: relative;
    height: calc(var(--otp-tile-h) * 2 + var(--otp-gap) + 2.5rem);
    margin-block: 1.6rem .4rem;
    overflow: visible;
    /* الأرقام تُقرأ من اليسار حتى داخل صفحة عربية، فالخانة الأولى هي اليسرى. */
    direction: ltr;
}

/* ── الختم ────────────────────────────────────────────────────────────────── */
.otp-seal {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
}

.otp-seal rect {
    fill: none;
    stroke-width: 1.5;
    stroke-linecap: round;
}

.otp-seal__track {
    stroke: var(--rg-line);
}

/*
    لون الدخول، لا لون الحكم.

    The seal is drawn while the server is still deciding, so it must not pre-announce the answer:
    it is the brand accent until there is a verdict, and only then does it become success or
    failure. A seal that went green before the response arrived would be a lie on a slow network.
*/
.otp-seal__draw {
    stroke: var(--brand-500);
    opacity: 0;
}

.otp.is-ok .otp-seal__draw {
    stroke: var(--rg-success);
}

.otp.is-bad .otp-seal__draw {
    stroke: var(--rg-danger);
}

/* ── الصفّ، والأشباح التي يقاس إليها ──────────────────────────────────────── */
.otp-rack,
.otp-ghosts {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

.otp-rack {
    display: flex;
    gap: var(--otp-gap);
}

/*
    الأشباح: شبكة ٢×٢ فارغة، لا تُرى، ووظيفتها أن تُقاس.

    The square arrangement is laid out by CSS and read back with getBoundingClientRect, so the
    travel distance is measured at the size the browser actually rendered. Hard-coded pixel
    offsets would be wrong at the next breakpoint, and wrong again once the webfont lands and
    the tiles reflow.
*/
.otp-ghosts {
    display: grid;
    grid-template-columns: repeat(2, var(--otp-tile-w));
    gap: var(--otp-gap);
    opacity: 0;
    pointer-events: none;
}

.otp-ghosts i {
    display: block;
    width: var(--otp-tile-w);
    height: var(--otp-tile-h);
}

/* ── الخانة ───────────────────────────────────────────────────────────────── */
.otp-slot {
    position: relative;
    width: var(--otp-tile-w);
    height: var(--otp-tile-h);
    border: 1px solid var(--rg-line);
    border-radius: var(--otp-radius);
    /* معتمة لا شفّافة: الختم يمرّ تحتها، فيجب أن تحجبه. */
    background: #fff;
    /* محور الدوران أسفل البطاقة بكثير: دورة واحدة تعطي الانتشار والقوس معًا. */
    transform-origin: 50% 340%;
    transition: border-color .28s ease, box-shadow .28s ease;
}

.otp-slot input {
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    outline: 0;
    border-radius: var(--otp-radius);
    background: transparent;
    color: var(--rg-ink);
    font: inherit;
    font-size: clamp(1.5rem, 5.5vw, 1.9rem);
    text-align: center;
    /* أرقام بعرض واحد: «١» و«٨» لا يغيّران عرض الصفّ. */
    font-variant-numeric: tabular-nums;
    caret-color: var(--brand-600);
    -webkit-appearance: none;
    appearance: none;
}

/* نفس حلقة التركيز التي تلبسها كل حقول هذه الصفحة. */
.otp-slot:has(input:focus-visible),
.otp-slot:has(input:focus) {
    border-color: var(--brand-500);
    box-shadow: 0 0 0 3px rgba(241, 137, 39, .22);
}

.otp-slot[data-filled] {
    border-color: var(--brand-200);
}

.otp.is-ok .otp-slot[data-filled] {
    border-color: var(--rg-success);
}

.otp.is-bad .otp-slot[data-filled] {
    border-color: var(--rg-danger);
}

/* ضوء يلفّ حافة الخانة عند كل ضغطة — يجعل الإدخال ميكانيكيًّا لا صامتًا. */
/*
    `width`/`height` صريحان — و`inset: 0` وحده لا يكفي.

    An `<svg>` is a *replaced* element, and an absolutely positioned replaced element with `auto`
    width resolves to its **intrinsic** size, not to its containing block — which for an SVG with
    no `width` attribute and no `viewBox` is the CSS default 300×150. So four of these sat 300px
    wide inside 50px tiles and pushed 11px of horizontal overflow onto the document at 320px:
    exactly the class of bug the honeypot's `left: -9999px` once caused, from a different cause.
    Measured at 320px, which is the width the brief asks this component to survive.
*/
.otp-lap {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
}

.otp-lap rect {
    fill: none;
    stroke: var(--brand-500);
    stroke-width: 1.5;
    opacity: 0;
}

/* ── الحكم: الختم الواحد، والهالة، والشرر ─────────────────────────────────── */
.otp-medal {
    position: absolute;
    left: 50%;
    top: 50%;
    display: grid;
    place-items: center;
    width: calc(var(--otp-tile-w) * 1.15);
    height: calc(var(--otp-tile-w) * 1.15);
    margin: calc(var(--otp-tile-w) * -.575) 0 0 calc(var(--otp-tile-w) * -.575);
    border: 1px solid var(--rg-success);
    border-radius: calc(var(--otp-radius) * 1.5);
    background: var(--rg-success-bg);
    opacity: 0;
    transform: scale(.5);
}

.otp-medal svg {
    width: 45%;
    height: 45%;
}

.otp-medal path {
    fill: none;
    stroke: var(--rg-success);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.otp-halo {
    position: absolute;
    left: 50%;
    top: 50%;
    width: calc(var(--otp-tile-w) * 2.1);
    height: calc(var(--otp-tile-w) * 2.1);
    margin: calc(var(--otp-tile-w) * -1.05) 0 0 calc(var(--otp-tile-w) * -1.05);
    opacity: 0;
}

.otp-halo circle {
    fill: none;
    stroke: var(--rg-success);
    stroke-width: 1;
    stroke-dasharray: 3 9;
    opacity: .45;
}

.otp-spark {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 1.5px;
    height: 9px;
    margin: -4px 0 0 -1px;
    border-radius: 2px;
    background: linear-gradient(var(--rg-success), transparent);
    opacity: 0;
    pointer-events: none;
}

/* ── الهزّة عند الرفض ─────────────────────────────────────────────────────── */
/*
    Three beats and done. A long shake reads as punishment, and the copy is already carrying the
    instruction — this only has to say «that did not go through» before the row comes back.
*/
.otp-shake {
    animation: otp-knock .42s cubic-bezier(.36, .07, .19, .97);
}

@keyframes otp-knock {
    10%, 90% { transform: translateX(-2px); }
    30%, 70% { transform: translateX(4px); }
    50% { transform: translateX(-6px); }
}

/* ── نصّ الحالة، والذيل ───────────────────────────────────────────────────── */
.otp-note {
    margin: 0;
    min-height: 1.6em;
    text-align: center;
    color: var(--rg-ink-500);
    font-size: .93rem;
}

.otp-note b {
    color: var(--rg-ink-700);
    font-weight: 400;
    /* رقم داخل جملة عربية يُقرأ من اليسار. */
    display: inline-block;
    direction: ltr;
    unicode-bidi: isolate;
}

.otp-foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: .4rem .9rem;
    margin-block-start: 1.3rem;
    min-height: 3.15rem;
}

.otp-resend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: .35rem .8rem;
}

/* شارة «موثّق» بعد القبول — تحمل الحكم بلا اعتماد على اللون وحده. */
.otp-guard {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    color: var(--rg-success);
    font-size: .93rem;
}

.otp-guard svg {
    width: .95rem;
    height: .95rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
}

/* حالة انتظار الشبكة: نبضة خفيفة، ولا نتيجة مُختلَقة. */
.otp-pending .otp-note {
    color: var(--rg-ink-700);
}

@media (prefers-reduced-motion: reduce) {
    .otp-shake {
        animation: none;
    }
}

/* The state line above the boxes on pages without vo's `.rg-head` (hr's recovery screen). */
.otp-state {
    display: grid;
    gap: .2rem;
    margin-block-end: .5rem;
    text-align: center;
}

.otp-state span {
    color: var(--rg-ink-500, #7c837b);
    font-size: .92rem;
}
