158 lines
8.0 KiB
CSS
158 lines
8.0 KiB
CSS
/* فونت از خودِ اپ سرو میشود؛ Google Fonts از داخلِ ایران کند یا مسدود است. */
|
||
@import 'vazirmatn/Vazirmatn-Variable-font-face.css';
|
||
@import 'tailwindcss';
|
||
|
||
/*
|
||
* پالتِ اپ — همان رنگهای طراحیِ اولیه، یکجا.
|
||
* هر رنگ یک کلاسِ Tailwind میسازد: bg-coral، text-ink، border-line، …
|
||
*/
|
||
@theme {
|
||
--font-sans: 'Vazirmatn', system-ui, sans-serif;
|
||
|
||
--color-coral: #ED6956; /* رنگِ اصلیِ برند */
|
||
--color-steel: #91ADC6; /* نقشه */
|
||
--color-teal: #97C0C0; /* درسها */
|
||
--color-violet: #9B7BFF; /* دستیار (پی) */
|
||
--color-orange: #FF7A3D; /* جدولِ امتیاز (دلتا) */
|
||
--color-indigo: #4C5FD5; /* سیگما */
|
||
--color-rose: #F3A9B0; /* اسپلش */
|
||
--color-gold: #E8A400;
|
||
--color-mint: #5FBF8F;
|
||
|
||
--color-ink: #1a1a2e; /* متنِ اصلی */
|
||
--color-body: #3a3a5c; /* متنِ بدنه */
|
||
/* متنِ کمرنگ. تا ۲۰۲۶-۱۰-۰۳ #9e9ec0 بود: کنتراست ۲٫۶ روی سفید، زیرِ حدِ ۴٫۵ِ WCAG، و
|
||
همین رنگ زیرنویسِ ۱۰–۱۲ پیکسلیِ همهی صفحهها و آیکونِ تبهای غیرفعال است. این یکی
|
||
روی سفید ۴٫۵ و روی پسزمینهی صفحه ۴٫۲ است. */
|
||
--color-muted: #727299;
|
||
--color-faint: #b0b0c8;
|
||
--color-line: #e9e6f7; /* حاشیهی کارت */
|
||
--color-soft: #f0eeff; /* پسزمینهی ملایم */
|
||
--color-page: #f6f5fb; /* پسزمینهی صفحه؛ کمی گرمتر از آبیِ قبلی */
|
||
}
|
||
|
||
@layer base {
|
||
html, body, #root {
|
||
height: 100%;
|
||
}
|
||
body {
|
||
margin: 0;
|
||
background: #14142a;
|
||
color: var(--color-ink);
|
||
font-family: var(--font-sans);
|
||
-webkit-font-smoothing: antialiased;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
button {
|
||
cursor: pointer;
|
||
font: inherit;
|
||
/* بیتأخیرِ دابلتپ و بیزوم با دو ضربه روی دکمهها */
|
||
touch-action: manipulation;
|
||
}
|
||
button:disabled {
|
||
cursor: not-allowed;
|
||
}
|
||
/* کشیدنِ صفحه به پایین در کروم اندروید کلِ اپ را رفرش میکرد (pull-to-refresh)
|
||
و وسطِ آزمون جوابِ جاری را میپراند؛ صفحههای داخلی خودشان اسکرول دارند. */
|
||
html, body {
|
||
overscroll-behavior: none;
|
||
}
|
||
/* حلقهی فوکوس فقط برای صفحهکلید (focus-visible)، نه برای لمس. */
|
||
:focus-visible {
|
||
outline: 2px solid var(--color-indigo);
|
||
outline-offset: 2px;
|
||
}
|
||
}
|
||
|
||
/* کاربری که در تنظیماتِ گوشی «کاهشِ حرکت» را زده: انیمیشنها و transitionهای CSS
|
||
تقریباً آنی میشوند. انیمیشنهای motion را MotionConfig در App خاموش میکند. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: 0.01ms !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
}
|
||
|
||
@layer base {
|
||
input, textarea {
|
||
font: inherit;
|
||
}
|
||
/* متنِ چندخطی (پاراگراف، گامهای درس، متنِ سوال، حبابِ پی) دوطرفچین است تا لبهی چپ
|
||
مرتب باشد. اینها در لایهی baseاند، پس هر جا کلاسِ text-center / text-left داده شده
|
||
برنده است و تیترها و برچسبهای کوتاه سرِ جایشان میمانند. خطِ آخر طبقِ معمول راستچین
|
||
میماند. text-justify فقط فاصلهی بینِ کلمهها را باز میکند (کشیدهنویسی در CSS نیست). */
|
||
p, li, .whitespace-pre-line {
|
||
text-align: justify;
|
||
text-justify: inter-word;
|
||
/* از تککلمهی یتیم در خطِ آخر جلوگیری میکند */
|
||
text-wrap: pretty;
|
||
}
|
||
/* پاراگرافِ داخلِ بخشِ وسطچین (حالتِ خالی، آنبوردینگ، اسپلش) وسطچین میماند؛
|
||
بدونِ این، قاعدهی بالا ارثِ text-center را میشکست. */
|
||
.text-center :is(p, li, .whitespace-pre-line) {
|
||
text-align: center;
|
||
}
|
||
.text-left :is(p, li, .whitespace-pre-line) {
|
||
text-align: left;
|
||
}
|
||
}
|
||
|
||
@utility no-scrollbar {
|
||
scrollbar-width: none;
|
||
&::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* فاصلهی امن بالای صفحه در گوشیهای ناچدار (وقتی اپ به صفحهی اصلی اضافه شده). */
|
||
@utility pt-safe {
|
||
padding-top: max(env(safe-area-inset-top), 0px);
|
||
}
|
||
@utility pb-safe {
|
||
padding-bottom: max(env(safe-area-inset-bottom), 0px);
|
||
}
|
||
|
||
/* نمادهای شناورِ سربرگ (ui.tsx). فقط transform، پس روی GPU میماند. */
|
||
@keyframes glyph-float {
|
||
0%, 100% { transform: translateY(0) rotate(-6deg); }
|
||
50% { transform: translateY(-7px) rotate(6deg); }
|
||
}
|
||
.glyph {
|
||
animation: glyph-float 6s ease-in-out infinite;
|
||
will-change: transform;
|
||
}
|
||
|
||
/* ناحیهی لمسِ بزرگتر بدونِ تغییرِ ظاهر: دکمههای کوچکِ سربرگ (۲۸–۳۲ پیکسل) برای انگشت کوچکاند. */
|
||
.hit { position: relative; }
|
||
.hit::after { content: ''; position: absolute; inset: -7px -3px; }
|
||
|
||
/* نوارِ اشتراک و نشانِ تدریسینو: فقط transform/opacity تا روی GPU بماند و روانی حفظ شود. */
|
||
@keyframes bar-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
|
||
.bar-fill { transform-origin: right; animation: bar-fill 0.9s cubic-bezier(.2,.8,.2,1) both; }
|
||
@keyframes shine { 0% { transform: translateX(120%); } 60%, 100% { transform: translateX(-220%); } }
|
||
.shine { animation: shine 3.2s ease-in-out infinite; will-change: transform; }
|
||
@keyframes spark { 0%, 100% { opacity: .25; transform: scale(.7); } 50% { opacity: 1; transform: scale(1); } }
|
||
.spark { animation: spark 2.4s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
|
||
|
||
/* نوارِ انتظارِ میانِ دو صفحه (MainApp → HoldBar). */
|
||
@keyframes hold-bar { 0% { transform: translateX(120%); } 100% { transform: translateX(-320%); } }
|
||
.hold-bar { animation: hold-bar 0.9s cubic-bezier(.4,0,.2,1) infinite; will-change: transform; }
|
||
|
||
/* شعلهی زنجیره (StreakFlame): دو لایه با ریتمِ متفاوت، از پایین تکان میخورند. */
|
||
@keyframes flame-a { 0%, 100% { transform: scale(1, 1) skewX(0deg); } 33% { transform: scale(.97, 1.04) skewX(-2deg); } 66% { transform: scale(1.02, .97) skewX(2deg); } }
|
||
@keyframes flame-b { 0%, 100% { transform: scale(1, 1); } 50% { transform: scale(.94, 1.08); } }
|
||
.flame-outer { transform-box: fill-box; transform-origin: 50% 100%; animation: flame-a 1.4s ease-in-out infinite; }
|
||
.flame-inner { transform-box: fill-box; transform-origin: 50% 100%; animation: flame-b .9s ease-in-out infinite; }
|
||
@keyframes ice-glint { 0%, 70%, 100% { opacity: 0; } 80% { opacity: 1; } }
|
||
.ice-glint { animation: ice-glint 2.8s ease-in-out infinite; }
|
||
@keyframes spark-rise { 0% { transform: translateY(0) scale(1); opacity: 0; } 15% { opacity: 1; } 100% { transform: translateY(-180px) scale(.3); opacity: 0; } }
|
||
.spark-rise { animation: spark-rise 1.8s ease-out infinite; }
|
||
|
||
/* همراههای کنارِ مسیرِ جزیرهها (IslandPath): فقط transform، تا روی گوشیِ ضعیف هم روی GPU بماند. */
|
||
@keyframes buddy-hop { 0%, 55%, 100% { transform: translateY(0) scale(1, 1); } 20% { transform: translateY(-12px) scale(.96, 1.05); } 40% { transform: translateY(0) scale(1.06, .94); } }
|
||
@keyframes buddy-sway { 0%, 100% { transform: rotate(-6deg); } 50% { transform: rotate(6deg); } }
|
||
.buddy-hop { animation: buddy-hop 1.6s ease-in-out infinite; transform-origin: 50% 100%; }
|
||
.buddy-sway { animation: buddy-sway 3.2s ease-in-out infinite; transform-origin: 50% 100%; }
|