diff --git a/public/fonts/Peyda-License.txt b/public/fonts/Peyda-License.txt new file mode 100644 index 0000000..02ea380 --- /dev/null +++ b/public/fonts/Peyda-License.txt @@ -0,0 +1,18 @@ +Peyda fonts are considered a proprietary software. +To gain information about the laws regarding the use of these fonts, please visit www.fontiran.com + +This set of fonts are used in this project under the license: (.....) + +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +In case you use this set of fonts in a software product, write the license code in the specified place and place this file next to the fonts. + +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +فونت های پیدا يک نرم افزار مالکيتی محسوب می شود جهت آگاهی از قوانين استفاده از اين فونت‌ها لطفاً به وبسايت +www.fontiran.com +مراجعه نماييد +اين مجموعه فونت تحت اجازه نامه +(.....) +در اين پروژه استفاده می شود + +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +در صورت استفاده از این مجموعه فونت در یک محصول نرم افزاری‌ یا وب‌سایت کد ۶ رقمی لایسنس خود را در محل مشخص شده (.....) درج کنید و این فایل را در کنار فونت ها قرار دهید. diff --git a/public/fonts/PeydaWeb-Black.woff2 b/public/fonts/PeydaWeb-Black.woff2 new file mode 100644 index 0000000..6e55558 Binary files /dev/null and b/public/fonts/PeydaWeb-Black.woff2 differ diff --git a/src/components/EducationSection.module.css b/src/components/EducationSection.module.css index 2fda710..8f72e72 100644 --- a/src/components/EducationSection.module.css +++ b/src/components/EducationSection.module.css @@ -45,6 +45,10 @@ transition: transform var(--motion-duration-default) var(--motion-ease-out); } +:global(html[dir='rtl']) .crestImage { + object-position: right top; +} + .institution { font-size: var(--font-size-body-small); line-height: var(--line-height-body-small); @@ -69,6 +73,7 @@ display: flex; flex-direction: column; justify-content: center; + text-align: start; } .degreeTitle { @@ -80,7 +85,8 @@ } .description { - max-width: 72ch; + width: 100%; + max-width: none; font-size: var(--font-size-body-default); line-height: var(--line-height-body-default); color: var(--text-secondary); @@ -90,7 +96,7 @@ background-color: var(--bg-raised); } -@media (max-width: 760px) { +@media (max-width: 900px) { .educationSection { padding-top: var(--space-8); } @@ -104,16 +110,14 @@ .crestCol, .crestContainer { - width: 11.25rem; - height: 8.25rem; + width: 100%; + height: auto; } -} -@media (max-width: 480px) { - .crestCol, - .crestContainer { - width: 9rem; - height: 6.75rem; + .crestImage { + width: 100%; + height: auto; + object-position: center; } } diff --git a/src/components/ExperienceSection.module.css b/src/components/ExperienceSection.module.css index 8fa3e15..e283feb 100644 --- a/src/components/ExperienceSection.module.css +++ b/src/components/ExperienceSection.module.css @@ -89,11 +89,17 @@ .itemContent { display: flex; flex-grow: 1; - padding-left: 1.25rem; + padding-inline-start: 1.25rem; padding-bottom: 2.25rem; gap: 2.25rem; } +.identityCol, +.detailsCol, +.roleTitles { + text-align: start; +} + @media (prefers-reduced-motion: reduce) { .nodeCurrent::after { animation: none; @@ -260,7 +266,7 @@ } .itemContent { - padding-left: var(--space-3); + padding-inline-start: var(--space-3); } .metaRow { diff --git a/src/components/HeroSection.module.css b/src/components/HeroSection.module.css index 915b91e..437ac1c 100644 --- a/src/components/HeroSection.module.css +++ b/src/components/HeroSection.module.css @@ -19,7 +19,8 @@ z-index: 10; display: flex; align-items: center; - justify-content: flex-start; + justify-content: space-between; + direction: ltr; padding: clamp(1.125rem, 2.5vw, 2rem) var(--layout-page-gutter) 0; } @@ -59,6 +60,95 @@ transition: transform var(--motion-duration-default) var(--motion-ease-out); } +.languageButton { + width: var(--size-icon-button); + min-width: 0; + padding: 0; +} + +.languageMenuWrapper { + position: relative; + direction: ltr; +} + +.languageMenu { + position: absolute; + top: calc(100% + 0.625rem); + right: 0; + display: flex; + width: 10.5rem; + flex-direction: column; + gap: 0.25rem; + padding: 0.375rem; + border: 1px solid var(--border-default); + border-radius: var(--radius-md); + background: color-mix(in srgb, var(--bg-raised) 96%, transparent); + box-shadow: 0 0.75rem 2rem var(--shadow-color); + opacity: 0; + visibility: hidden; + pointer-events: none; + transform: translateY(-0.375rem) scale(0.98); + transform-origin: top right; + transition: + opacity var(--motion-duration-fast) ease, + transform var(--motion-duration-fast) var(--motion-ease-out), + visibility 0s linear var(--motion-duration-fast); + backdrop-filter: blur(14px); +} + +.languageMenu[data-open='true'] { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translateY(0) scale(1); + transition-delay: 0s; +} + +.languageOption { + display: flex; + min-height: 2.75rem; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + padding: 0.625rem 0.75rem; + border-radius: var(--radius-sm); + color: var(--text-primary); + font-family: 'IRANYekanXVF', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + font-size: 0.875rem; + font-weight: 600; + text-decoration: none; + transition: background-color var(--motion-duration-fast) ease; +} + +.languageOption[aria-current='page'] { + background: var(--bg-subtle); + color: var(--action-link); +} + +.languageOptionLabel { + display: inline-flex; + align-items: center; + gap: 0.5rem; +} + +.languageFlag { + display: block; + width: 1.25rem; + height: 1.25rem; + flex: 0 0 auto; +} + +:global(html[dir='rtl']) .languageMenu { + direction: rtl; + text-align: right; +} + +:global(html[dir='rtl']) .languageOption, +:global(html[dir='rtl']) .languageOptionLabel { + direction: rtl; + text-align: right; +} + .themeIcon { fill: currentColor; } @@ -138,6 +228,10 @@ white-space: nowrap; } +:global(html[dir='rtl']) .heroName { + white-space: normal; +} + /* ── Subtitle ───────────────────────────────────────────────── */ .heroSubtitle { margin-bottom: clamp(1.25rem, 2.8vw, 2.25rem); @@ -378,6 +472,7 @@ gap: 0.5rem; } + .heroButtonPrimary { grid-column: 1 / -1; } @@ -395,6 +490,10 @@ HOVER STATES ================================================================ */ @media (hover: hover) and (pointer: fine) { + .languageOption:hover { + background: var(--bg-subtle); + } + .iconButton:hover { color: var(--color-blue-500); border-color: var(--accent-default); diff --git a/src/components/IsometricFooter/IsometricFooter.module.css b/src/components/IsometricFooter/IsometricFooter.module.css index bf28185..d2cb8db 100644 --- a/src/components/IsometricFooter/IsometricFooter.module.css +++ b/src/components/IsometricFooter/IsometricFooter.module.css @@ -39,6 +39,11 @@ animation: footerFadeIn 0.7s ease-out forwards; } +:global(html[lang='fa']) .headerTitle { + font-family: 'Peyda', 'IRANYekanXVF', sans-serif; + font-weight: 900; +} + .headerSubtitle { font-size: clamp(0.85rem, 2.5vw, 1.05rem); color: var(--text-secondary); @@ -91,6 +96,8 @@ } .emailText { + direction: ltr; + unicode-bidi: isolate; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -245,7 +252,7 @@ display: flex; flex-direction: column; min-width: 0; - text-align: left; + text-align: start; } .contactCardName { @@ -258,6 +265,8 @@ } .contactCardLabel { + direction: ltr; + unicode-bidi: isolate; font-size: 0.88rem; font-weight: 600; color: var(--text-primary); diff --git a/src/components/MilestonesSection.module.css b/src/components/MilestonesSection.module.css index 69246d1..4227abf 100644 --- a/src/components/MilestonesSection.module.css +++ b/src/components/MilestonesSection.module.css @@ -46,6 +46,7 @@ display: flex; flex-direction: column; position: relative; + text-align: start; } .titleRow { diff --git a/src/components/ProjectsSection.module.css b/src/components/ProjectsSection.module.css index 61b9200..80e9843 100644 --- a/src/components/ProjectsSection.module.css +++ b/src/components/ProjectsSection.module.css @@ -149,6 +149,7 @@ flex: 1; flex-direction: column; padding: 1.125rem 1rem; + text-align: start; } .cardTitleRow { @@ -292,6 +293,11 @@ will-change: transform; } +:global(html[dir='rtl']) .scrollbarThumb { + right: 0; + left: auto; +} + .scrollbarDragging .scrollbarThumb { cursor: grabbing; } @@ -526,6 +532,10 @@ transform: translateX(3px); } + :global(html[dir='rtl']) .caseStudyLink:hover svg { + transform: translateX(-3px); + } + .navButtonPrevious:hover:not(:disabled) svg { transform: translateX(-2px); } diff --git a/src/components/RiverJourney.module.css b/src/components/RiverJourney.module.css index ffd477e..8501f90 100644 --- a/src/components/RiverJourney.module.css +++ b/src/components/RiverJourney.module.css @@ -21,13 +21,14 @@ .introBlock { position: absolute; top: 7rem; - left: var(--layout-page-gutter); + inset-inline-start: var(--layout-page-gutter); z-index: 3; width: min(24.375rem, calc(100% - (2 * var(--layout-page-gutter)))); padding: var(--space-4); border-radius: var(--radius-md); background: var(--overlay-contact); backdrop-filter: blur(8px); + text-align: start; } .eyebrow { @@ -58,6 +59,10 @@ transform-origin: left center; } +:global(html[dir='rtl']) .underlineBar { + transform-origin: right center; +} + .introBlock[data-visible='true'] .underlineBar { animation: aboutUnderlineIn 500ms var(--motion-ease-out) 120ms both; } @@ -83,13 +88,13 @@ .stepsList { position: absolute; top: 5.5rem; - right: var(--layout-page-gutter); + inset-inline-end: var(--layout-page-gutter); z-index: 3; display: flex; flex-direction: column; width: min(33rem, 46vw); gap: var(--space-3); - padding-left: 3.5rem; + padding-inline-start: 3.5rem; list-style: none; } @@ -98,7 +103,7 @@ position: absolute; top: 1.25rem; bottom: 1.25rem; - left: 1.25rem; + inset-inline-start: 1.25rem; width: 2px; border-radius: var(--radius-pill); background: var(--border-strong); @@ -117,12 +122,13 @@ box-shadow: 0 0.5rem 1.5rem var(--shadow-color); backdrop-filter: blur(8px); transition: border-color var(--motion-duration-fast) ease; + text-align: start; } .stepIcon { position: absolute; top: 1rem; - left: -3.5rem; + inset-inline-start: -3.5rem; z-index: 1; display: inline-flex; align-items: center; @@ -164,6 +170,7 @@ } .stepContent { + width: 100%; min-width: 0; } @@ -188,7 +195,7 @@ .stepsList { top: 4.5rem; - right: 2rem; + inset-inline-end: 2rem; width: min(28rem, 45vw); } } @@ -208,7 +215,7 @@ .introBlock { position: relative; top: auto; - left: auto; + inset-inline-start: auto; width: 100%; padding: var(--space-4); border: 1px solid var(--border-default); @@ -230,13 +237,13 @@ .stepsList { position: relative; top: auto; - right: auto; + inset-inline-end: auto; display: flex; flex-direction: column; width: 100%; gap: var(--space-3); margin-top: 13.75rem; - padding-left: 3.25rem; + padding-inline-start: 3.25rem; } .stepsList::before { @@ -244,7 +251,7 @@ position: absolute; top: 1.25rem; bottom: 1.25rem; - left: 1.125rem; + inset-inline-start: 1.125rem; width: 2px; background: var(--border-strong); } @@ -260,7 +267,7 @@ .stepIcon { top: 0.75rem; - left: -3.25rem; + inset-inline-start: -3.25rem; } } diff --git a/src/components/ScenicHeroJourney.module.css b/src/components/ScenicHeroJourney.module.css index c6a4b3a..3bec7f8 100644 --- a/src/components/ScenicHeroJourney.module.css +++ b/src/components/ScenicHeroJourney.module.css @@ -100,6 +100,11 @@ animation: landscapeFloatLeft 8s var(--motion-ease-in-out) infinite alternate; } +:global(html[dir='rtl']) .heroBackdrop { + transform: scaleX(-1); + transform-origin: center; +} + .backdropRight { top: clamp(80px, 9vw, 110px); right: clamp(-240px, calc(22% - 29vw), -40px); diff --git a/src/components/SiteLoader.module.css b/src/components/SiteLoader.module.css index 4b7b167..b3b7c8d 100644 --- a/src/components/SiteLoader.module.css +++ b/src/components/SiteLoader.module.css @@ -169,11 +169,15 @@ transition: transform 180ms var(--motion-ease-out); } +:global(html[dir='rtl']) .progressFill { + transform-origin: right center; +} + .progressValue { color: var(--text-secondary); font-size: 0.8125rem; font-variant-numeric: tabular-nums; - text-align: right; + text-align: end; } @keyframes riverArrive { diff --git a/src/components/SkillsSection.module.css b/src/components/SkillsSection.module.css index dc25f24..619048e 100644 --- a/src/components/SkillsSection.module.css +++ b/src/components/SkillsSection.module.css @@ -70,6 +70,10 @@ transform-origin: left center; } +:global(html[dir='rtl']) .segmentFilled { + transform-origin: right center; +} + .mainCard[data-visible='true'] .segmentFilled { animation: meterSegmentIn 400ms var(--motion-ease-out) both; } diff --git a/src/styles/globals.css b/src/styles/globals.css index 4fe72b7..ce2e05f 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -25,6 +25,14 @@ html[data-theme="dark"] { color-scheme: dark; } +@font-face { + font-family: 'Peyda'; + src: url('/fonts/PeydaWeb-Black.woff2') format('woff2'); + font-style: normal; + font-weight: 900; + font-display: swap; +} + html[data-site-loading='true'] { overflow: hidden; } @@ -42,6 +50,16 @@ body { transition: background-color 0.25s ease, color 0.25s ease; } +html[dir='rtl'] body { + text-align: right; +} + +.ltr-token { + direction: ltr; + unicode-bidi: isolate; + font-family: 'IRANYekanXVF', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; +} + /* Master 1440px Fixed Canvas */ .desktop-canvas { width: 100%; @@ -126,6 +144,20 @@ body { transform-origin: left center; } +html[dir='rtl'] .section-header { + align-items: flex-start; + text-align: right; +} + +html[dir='rtl'] .section-eyebrow, +html[dir='rtl'] .section-title { + letter-spacing: 0; +} + +html[dir='rtl'] .section-underline { + transform-origin: right center; +} + /* One-time viewport reveals. Content remains visible when JavaScript is unavailable. */ html[data-motion-ready='true'] [data-reveal] { opacity: 0; @@ -170,12 +202,22 @@ html[data-motion-ready='true'] [data-visible='true'] .section-underline { } } -/* Pacifico Display Font Class */ +/* Keep the original Latin display face and use Peyda for Persian text. */ .font-display { font-family: 'Pacifico', cursive; font-weight: 400; } +html[lang='fa'] .font-display { + font-family: 'Peyda', 'IRANYekanXVF', sans-serif; + font-weight: 900; +} + +html[lang='fa'] .section-title { + font-family: 'Peyda', 'IRANYekanXVF', sans-serif; + font-weight: 900; +} + /* Pill Button */ .pill-btn { display: inline-flex; @@ -188,6 +230,7 @@ html[data-motion-ready='true'] [data-visible='true'] .section-underline { font-size: var(--font-size-button); line-height: var(--line-height-button); font-weight: 600; + font-family: 'IRANYekanXVF', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; cursor: pointer; text-decoration: none; transition: