Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
E
EL-Captain-Website
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
EL-Captain-Website
Commits
081d9938
Commit
081d9938
authored
Jun 18, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
kokokokoko
parent
8c142ce1
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
710 additions
and
249 deletions
+710
-249
base.css
public/css/base.css
+19
-6
components.css
public/css/components.css
+204
-108
home.css
public/css/home.css
+217
-48
pages.css
public/css/pages.css
+95
-39
tokens.css
public/css/tokens.css
+48
-37
main.js
public/js/main.js
+127
-11
No files found.
public/css/base.css
View file @
081d9938
...
...
@@ -32,7 +32,7 @@ html {
body
{
font-family
:
'Tajawal'
,
sans-serif
;
font-size
:
var
(
--text-base
);
line-height
:
1.7
;
line-height
:
1.7
5
;
color
:
var
(
--ink-primary
);
background-color
:
var
(
--bg-deep
);
overflow-x
:
hidden
;
...
...
@@ -40,14 +40,15 @@ body {
}
h1
,
h2
,
h3
,
h4
,
h5
,
h6
{
line-height
:
1.2
5
;
line-height
:
1.2
;
font-weight
:
700
;
text-wrap
:
balance
;
letter-spacing
:
-0.01em
;
}
h1
{
font-size
:
var
(
--text-5xl
);
font-weight
:
900
;
}
h2
{
font-size
:
var
(
--text-4xl
);
font-weight
:
800
;
}
h3
{
font-size
:
var
(
--text-3xl
);
}
h1
{
font-size
:
var
(
--text-5xl
);
font-weight
:
900
;
letter-spacing
:
-0.025em
;
line-height
:
1.05
;
}
h2
{
font-size
:
var
(
--text-4xl
);
font-weight
:
800
;
letter-spacing
:
-0.02em
;
line-height
:
1.1
;
}
h3
{
font-size
:
var
(
--text-3xl
);
letter-spacing
:
-0.015em
;
}
h4
{
font-size
:
var
(
--text-2xl
);
}
h5
{
font-size
:
var
(
--text-xl
);
}
...
...
@@ -86,7 +87,7 @@ ul, ol {
}
::selection
{
background
:
oklch
(
0.5
70
0.158
353.3
/
0.3
);
background
:
oklch
(
0.5
50
0.180
353.3
/
0.35
);
color
:
var
(
--ink-primary
);
}
...
...
@@ -109,6 +110,7 @@ ul, ol {
.section
{
padding-block
:
var
(
--space-section
);
position
:
relative
;
}
.visually-hidden
{
...
...
@@ -122,3 +124,14 @@ ul, ol {
white-space
:
nowrap
;
border
:
0
;
}
/* Noise texture overlay for depth */
body
::before
{
content
:
''
;
position
:
fixed
;
inset
:
0
;
pointer-events
:
none
;
z-index
:
9999
;
opacity
:
0.025
;
background-image
:
url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)
'
/
%
3
E
%
3
C
/
svg
%
3
E
"
);
}
public/css/components.css
View file @
081d9938
This diff is collapsed.
Click to expand it.
public/css/home.css
View file @
081d9938
This diff is collapsed.
Click to expand it.
public/css/pages.css
View file @
081d9938
/* Module detail pages */
.module-detail__features
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
min
(
100%
,
30
0px
),
1
fr
));
gap
:
var
(
--space-
lg
);
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
min
(
100%
,
28
0px
),
1
fr
));
gap
:
var
(
--space-
md
);
}
.module-detail__screenshot-section
{
...
...
@@ -42,25 +42,27 @@
display
:
flex
;
align-items
:
flex-start
;
gap
:
var
(
--space-sm
);
font-size
:
var
(
--text-
base
);
font-size
:
var
(
--text-
sm
);
color
:
var
(
--ink-secondary
);
line-height
:
1.7
;
}
.module-detail__list-item
::before
{
content
:
''
;
flex-shrink
:
0
;
width
:
6
px
;
height
:
6
px
;
margin-top
:
0.6
5
em
;
width
:
5
px
;
height
:
5
px
;
margin-top
:
0.6em
;
background
:
var
(
--brand-primary
);
border-radius
:
50%
;
box-shadow
:
0
0
6px
oklch
(
0.550
0.180
353.3
/
0.4
);
}
/* Features overview page */
.features-grid
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
min
(
100%
,
3
2
0px
),
1
fr
));
gap
:
var
(
--space-
lg
);
grid-template-columns
:
repeat
(
auto-fit
,
minmax
(
min
(
100%
,
3
0
0px
),
1
fr
));
gap
:
var
(
--space-
md
);
}
.feature-item
{
...
...
@@ -70,27 +72,41 @@
padding
:
var
(
--space-xl
);
background
:
var
(
--bg-surface
);
border
:
1px
solid
var
(
--border-subtle
);
border-radius
:
var
(
--radius-
lg
);
border-radius
:
var
(
--radius-
xl
);
align-items
:
start
;
transition
:
all
var
(
--duration-slow
)
var
(
--ease-out-expo
);
position
:
relative
;
overflow
:
hidden
;
}
.feature-item
:hover
{
border-color
:
var
(
--border-glow
);
transform
:
translateY
(
-2px
);
box-shadow
:
0
8px
32px
oklch
(
0
0
0
/
0.2
);
}
.feature-item__number
{
font-size
:
var
(
--text-
3
xl
);
font-size
:
var
(
--text-
2
xl
);
font-weight
:
900
;
color
:
var
(
--brand-primary-dim
);
background
:
linear-gradient
(
135deg
,
var
(
--brand-primary-dim
),
var
(
--brand-primary-muted
));
-webkit-background-clip
:
text
;
-webkit-text-fill-color
:
transparent
;
background-clip
:
text
;
line-height
:
1
;
opacity
:
0.
6
;
opacity
:
0.
7
;
}
.feature-item__content
h4
{
font-size
:
var
(
--text-lg
);
margin-bottom
:
var
(
--space-xs
);
font-size
:
var
(
--text-base
);
font-weight
:
700
;
margin-bottom
:
var
(
--space-2xs
);
color
:
var
(
--ink-primary
);
}
.feature-item__content
p
{
font-size
:
var
(
--text-sm
);
color
:
var
(
--ink-secondary
);
line-height
:
1.8
;
}
/* Contact / Pricing page */
...
...
@@ -116,7 +132,7 @@
.form-group
{
display
:
flex
;
flex-direction
:
column
;
gap
:
var
(
--space-xs
);
gap
:
var
(
--space-
2
xs
);
}
.form-group
label
{
...
...
@@ -128,14 +144,15 @@
.form-group
input
,
.form-group
textarea
,
.form-group
select
{
padding
:
var
(
--space-md
);
padding
:
var
(
--space-
sm
)
var
(
--space-
md
);
background
:
var
(
--bg-surface
);
border
:
1px
solid
var
(
--border-
default
);
border-radius
:
var
(
--radius-
md
);
border
:
1px
solid
var
(
--border-
subtle
);
border-radius
:
var
(
--radius-
lg
);
color
:
var
(
--ink-primary
);
font-family
:
inherit
;
font-size
:
var
(
--text-base
);
transition
:
border-color
var
(
--duration-fast
)
var
(
--ease-out-quart
);
font-size
:
var
(
--text-sm
);
transition
:
all
var
(
--duration-fast
)
var
(
--ease-out-quart
);
min-height
:
44px
;
}
.form-group
input
:focus
,
...
...
@@ -143,19 +160,33 @@
.form-group
select
:focus
{
outline
:
none
;
border-color
:
var
(
--brand-primary
);
box-shadow
:
0
0
0
3px
oklch
(
0.570
0.158
353.3
/
0.15
);
box-shadow
:
0
0
0
3px
oklch
(
0.550
0.180
353.3
/
0.1
),
0
0
20px
oklch
(
0.550
0.180
353.3
/
0.05
);
background
:
var
(
--bg-elevated
);
}
.form-group
input
::placeholder
,
.form-group
textarea
::placeholder
{
color
:
var
(
--ink-muted
);
}
.form-group
textarea
{
min-height
:
1
5
0px
;
min-height
:
1
2
0px
;
resize
:
vertical
;
}
.form-group
select
{
appearance
:
none
;
background-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%23888' viewBox='0 0 16 16'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E")
;
background-repeat
:
no-repeat
;
background-position
:
left
12px
center
;
padding-left
:
36px
;
}
.contact-info
{
padding
:
var
(
--space-xl
);
background
:
var
(
--bg-surface
);
border
:
1px
solid
var
(
--border-subtle
);
border-radius
:
var
(
--radius-
lg
);
border-radius
:
var
(
--radius-
xl
);
}
.contact-info__item
{
...
...
@@ -170,24 +201,25 @@
}
.contact-info__icon
{
width
:
2.5rem
;
height
:
2.5rem
;
width
:
2.
2
5rem
;
height
:
2.
2
5rem
;
display
:
grid
;
place-items
:
center
;
background
:
oklch
(
0.570
0.158
353.3
/
0.1
);
background
:
oklch
(
0.550
0.180
353.3
/
0.08
);
border
:
1px
solid
oklch
(
0.550
0.180
353.3
/
0.15
);
border-radius
:
var
(
--radius-md
);
color
:
var
(
--brand-primary-light
);
flex-shrink
:
0
;
}
.contact-info__label
{
font-size
:
var
(
--text-
sm
);
font-size
:
var
(
--text-
xs
);
color
:
var
(
--ink-muted
);
margin-bottom
:
2px
;
}
.contact-info__value
{
font-size
:
var
(
--text-
base
);
font-size
:
var
(
--text-
sm
);
color
:
var
(
--ink-primary
);
font-weight
:
500
;
}
...
...
@@ -196,7 +228,7 @@
.workflow
{
display
:
flex
;
align-items
:
center
;
gap
:
var
(
--space-
md
);
gap
:
var
(
--space-
sm
);
flex-wrap
:
wrap
;
justify-content
:
center
;
padding
:
var
(
--space-xl
)
0
;
...
...
@@ -205,19 +237,27 @@
.workflow__step
{
display
:
flex
;
align-items
:
center
;
gap
:
var
(
--space-
sm
);
padding
:
var
(
--space-
md
)
var
(
--space-lg
);
gap
:
var
(
--space-
xs
);
padding
:
var
(
--space-
sm
)
var
(
--space-md
);
background
:
var
(
--bg-surface
);
border
:
1px
solid
var
(
--border-subtle
);
border-radius
:
var
(
--radius-
md
);
border-radius
:
var
(
--radius-
lg
);
font-size
:
var
(
--text-sm
);
font-weight
:
500
;
color
:
var
(
--ink-primary
);
transition
:
all
var
(
--duration-normal
)
var
(
--ease-out-quart
);
white-space
:
nowrap
;
}
.workflow__step
:hover
{
border-color
:
var
(
--border-glow
);
box-shadow
:
0
0
16px
oklch
(
0.550
0.180
353.3
/
0.1
);
}
.workflow__arrow
{
color
:
var
(
--brand-primary-dim
);
font-size
:
var
(
--text-xl
);
font-size
:
var
(
--text-lg
);
opacity
:
0.6
;
}
/* Comparison table */
...
...
@@ -225,15 +265,15 @@
width
:
100%
;
border-collapse
:
collapse
;
background
:
var
(
--bg-surface
);
border-radius
:
var
(
--radius-
lg
);
border-radius
:
var
(
--radius-
xl
);
overflow
:
hidden
;
border
:
1px
solid
var
(
--border-subtle
);
}
.comparison-table
th
{
padding
:
var
(
--space-
md
)
var
(
--space-lg
);
padding
:
var
(
--space-
sm
)
var
(
--space-md
);
background
:
var
(
--bg-elevated
);
font-weight
:
7
00
;
font-weight
:
6
00
;
font-size
:
var
(
--text-sm
);
color
:
var
(
--ink-primary
);
text-align
:
right
;
...
...
@@ -241,10 +281,15 @@
}
.comparison-table
td
{
padding
:
var
(
--space-
md
)
var
(
--space-lg
);
padding
:
var
(
--space-
sm
)
var
(
--space-md
);
font-size
:
var
(
--text-sm
);
color
:
var
(
--ink-secondary
);
border-bottom
:
1px
solid
var
(
--border-subtle
);
border-bottom
:
1px
solid
oklch
(
1
0
0
/
0.02
);
transition
:
background
var
(
--duration-fast
)
var
(
--ease-out-quart
);
}
.comparison-table
tr
:hover
td
{
background
:
oklch
(
1
0
0
/
0.02
);
}
.comparison-table
tr
:last-child
td
{
...
...
@@ -258,5 +303,16 @@
.comparison-table
.cross
{
color
:
var
(
--ink-muted
);
opacity
:
0.5
;
opacity
:
0.4
;
}
/* Gradient section dividers */
.section--gradient-top
::before
{
content
:
''
;
position
:
absolute
;
top
:
0
;
left
:
0
;
right
:
0
;
height
:
1px
;
background
:
linear-gradient
(
90deg
,
transparent
10%
,
var
(
--brand-primary-dim
),
transparent
90%
);
}
public/css/tokens.css
View file @
081d9938
:root
{
/* Brand seed: oklch(0.570 0.158 353.3) — deep rose/crimson */
--brand-primary
:
oklch
(
0.570
0.158
353.3
);
--brand-primary-light
:
oklch
(
0.650
0.140
353.3
);
--brand-primary-dim
:
oklch
(
0.450
0.120
353.3
);
--brand-primary-muted
:
oklch
(
0.380
0.080
353.3
);
--brand-primary
:
oklch
(
0.550
0.180
353.3
);
--brand-primary-light
:
oklch
(
0.680
0.160
353.3
);
--brand-primary-dim
:
oklch
(
0.400
0.130
353.3
);
--brand-primary-muted
:
oklch
(
0.320
0.090
353.3
);
--brand-primary-ghost
:
oklch
(
0.570
0.158
353.3
/
0.06
);
/* Backgrounds —
true dark, not warm-tinted
*/
--bg-deep
:
oklch
(
0.
120
0.008
353.3
);
--bg-base
:
oklch
(
0.1
50
0.010
353.3
);
--bg-surface
:
oklch
(
0.1
85
0.012
353.3
);
--bg-elevated
:
oklch
(
0.
220
0.014
353.3
);
/* Backgrounds —
deeper, colder dark
*/
--bg-deep
:
oklch
(
0.
095
0.006
280
);
--bg-base
:
oklch
(
0.1
15
0.008
280
);
--bg-surface
:
oklch
(
0.1
45
0.010
280
);
--bg-elevated
:
oklch
(
0.
180
0.012
280
);
/* Text */
--ink-primary
:
oklch
(
0.9
50
0.005
353.3
);
--ink-secondary
:
oklch
(
0.7
50
0.010
353.3
);
--ink-muted
:
oklch
(
0.5
50
0.008
353.3
);
--ink-primary
:
oklch
(
0.9
60
0.003
280
);
--ink-secondary
:
oklch
(
0.7
20
0.008
280
);
--ink-muted
:
oklch
(
0.5
00
0.006
280
);
/* Borders */
--border-subtle
:
oklch
(
0.250
0.012
353.3
);
--border-default
:
oklch
(
0.320
0.015
353.3
);
--border-subtle
:
oklch
(
0.200
0.010
280
);
--border-default
:
oklch
(
0.280
0.012
280
);
--border-glow
:
oklch
(
0.550
0.180
353.3
/
0.25
);
/* Accent —
gold for secondary highlights
*/
--accent-gold
:
oklch
(
0.7
00
0.12
0
85
);
/* Accent —
warm gold for secondary
*/
--accent-gold
:
oklch
(
0.7
50
0.13
0
85
);
--accent-gold-dim
:
oklch
(
0.550
0.090
85
);
/* Functional */
...
...
@@ -32,45 +34,54 @@
/* Typography scale — 1.333 ratio (perfect fourth) */
--text-xs
:
clamp
(
0.694rem
,
0.65
vi
+
0.5rem
,
0.75rem
);
--text-sm
:
clamp
(
0.833rem
,
0.8
vi
+
0.6rem
,
0.875rem
);
--text-base
:
clamp
(
1rem
,
1
vi
+
0.7rem
,
1.063
rem
);
--text-lg
:
clamp
(
1.
125rem
,
1.2
vi
+
0.8rem
,
1.25
rem
);
--text-xl
:
clamp
(
1.
333rem
,
1.5
vi
+
0.9rem
,
1.
5rem
);
--text-2xl
:
clamp
(
1.
777rem
,
2.2
vi
+
1rem
,
2.25
rem
);
--text-3xl
:
clamp
(
2.
369rem
,
3
vi
+
1.2rem
,
3
rem
);
--text-4xl
:
clamp
(
3.157rem
,
4
vi
+
1.5rem
,
4
rem
);
--text-5xl
:
clamp
(
4.209rem
,
5.5
vi
+
1.8rem
,
5
.5rem
);
--text-base
:
clamp
(
0.938rem
,
0.9
vi
+
0.65rem
,
1
rem
);
--text-lg
:
clamp
(
1.
063rem
,
1.1
vi
+
0.75rem
,
1.188
rem
);
--text-xl
:
clamp
(
1.
25rem
,
1.4
vi
+
0.85rem
,
1.37
5rem
);
--text-2xl
:
clamp
(
1.
6rem
,
2
vi
+
0.95rem
,
2
rem
);
--text-3xl
:
clamp
(
2.
1rem
,
2.8
vi
+
1.1rem
,
2.75
rem
);
--text-4xl
:
clamp
(
2.8rem
,
3.5
vi
+
1.3rem
,
3.5
rem
);
--text-5xl
:
clamp
(
3.4rem
,
4.5
vi
+
1.5rem
,
4
.5rem
);
/* Spacing */
--space-2xs
:
clamp
(
0.125rem
,
0.25
vi
,
0.25rem
);
--space-xs
:
clamp
(
0.25rem
,
0.5
vi
,
0.5rem
);
--space-sm
:
clamp
(
0.5rem
,
1
vi
,
0.75rem
);
--space-md
:
clamp
(
1rem
,
2
vi
,
1.
5rem
);
--space-lg
:
clamp
(
1.
5rem
,
3
vi
,
2.5
rem
);
--space-xl
:
clamp
(
2
.5rem
,
5
vi
,
4
rem
);
--space-2xl
:
clamp
(
4rem
,
8
vi
,
6
rem
);
--space-3xl
:
clamp
(
6rem
,
12
vi
,
10
rem
);
--space-md
:
clamp
(
0.875rem
,
1.5
vi
,
1.2
5rem
);
--space-lg
:
clamp
(
1.
25rem
,
2.5
vi
,
2
rem
);
--space-xl
:
clamp
(
2
rem
,
4
vi
,
3
rem
);
--space-2xl
:
clamp
(
3rem
,
6
vi
,
5
rem
);
--space-3xl
:
clamp
(
5rem
,
10
vi
,
8
rem
);
--space-section
:
clamp
(
5rem
,
10
vi
,
8rem
);
/* Radius */
--radius-xs
:
0.125rem
;
--radius-sm
:
0.25rem
;
--radius-md
:
0.5rem
;
--radius-lg
:
1rem
;
--radius-xl
:
1.5rem
;
--radius-lg
:
0.875rem
;
--radius-xl
:
1.25rem
;
--radius-2xl
:
1.75rem
;
/* Shadows */
--shadow-sm
:
0
1px
3px
oklch
(
0
0
0
/
0.3
);
--shadow-md
:
0
4px
12px
oklch
(
0
0
0
/
0.4
);
--shadow-lg
:
0
8px
32px
oklch
(
0
0
0
/
0.5
);
--shadow-glow
:
0
0
40px
oklch
(
0.570
0.158
353.3
/
0.2
);
--shadow-sm
:
0
1px
3px
oklch
(
0
0
0
/
0.4
);
--shadow-md
:
0
4px
16px
oklch
(
0
0
0
/
0.5
);
--shadow-lg
:
0
12px
48px
oklch
(
0
0
0
/
0.6
);
--shadow-glow
:
0
0
60px
oklch
(
0.550
0.180
353.3
/
0.15
);
--shadow-glow-strong
:
0
0
80px
oklch
(
0.550
0.180
353.3
/
0.25
);
--shadow-inner
:
inset
0
1px
0
oklch
(
1
0
0
/
0.04
);
/* Transitions */
--ease-out-quart
:
cubic-bezier
(
0.25
,
1
,
0.5
,
1
);
--ease-out-expo
:
cubic-bezier
(
0.16
,
1
,
0.3
,
1
);
--ease-out-back
:
cubic-bezier
(
0.34
,
1.56
,
0.64
,
1
);
--ease-spring
:
cubic-bezier
(
0.175
,
0.885
,
0.32
,
1.275
);
--duration-fast
:
150ms
;
--duration-normal
:
300ms
;
--duration-slow
:
500ms
;
--duration-slower
:
800ms
;
/* Z-index scale */
--z-base
:
0
;
--z-above
:
10
;
--z-dropdown
:
100
;
--z-sticky
:
200
;
--z-modal-backdrop
:
300
;
...
...
@@ -79,7 +90,7 @@
--z-tooltip
:
600
;
/* Layout */
--container-max
:
12
8
0px
;
--container-wide
:
14
4
0px
;
--content-max
:
7
5ch
;
--container-max
:
12
0
0px
;
--container-wide
:
14
0
0px
;
--content-max
:
6
5ch
;
}
public/js/main.js
View file @
081d9938
...
...
@@ -2,6 +2,9 @@ document.addEventListener('DOMContentLoaded', () => {
initNav
();
initReveal
();
initMobileMenu
();
initParallax
();
initCountUp
();
initSmoothHover
();
});
function
initNav
()
{
...
...
@@ -12,12 +15,14 @@ function initNav() {
window
.
addEventListener
(
'scroll'
,
()
=>
{
if
(
!
ticking
)
{
requestAnimationFrame
(()
=>
{
nav
.
classList
.
toggle
(
'nav--scrolled'
,
window
.
scrollY
>
5
0
);
nav
.
classList
.
toggle
(
'nav--scrolled'
,
window
.
scrollY
>
3
0
);
ticking
=
false
;
});
ticking
=
true
;
}
},
{
passive
:
true
});
if
(
window
.
scrollY
>
30
)
nav
.
classList
.
add
(
'nav--scrolled'
);
}
function
initMobileMenu
()
{
...
...
@@ -32,7 +37,7 @@ function initMobileMenu() {
});
links
.
addEventListener
(
'click'
,
(
e
)
=>
{
if
(
e
.
target
.
classList
.
contains
(
'nav__link'
))
{
if
(
e
.
target
.
classList
.
contains
(
'nav__link'
)
||
e
.
target
.
classList
.
contains
(
'nav__cta'
)
)
{
links
.
classList
.
remove
(
'nav__links--open'
);
toggle
.
setAttribute
(
'aria-expanded'
,
'false'
);
document
.
body
.
style
.
overflow
=
''
;
...
...
@@ -41,7 +46,12 @@ function initMobileMenu() {
}
function
initReveal
()
{
if
(
window
.
matchMedia
(
'(prefers-reduced-motion: reduce)'
).
matches
)
return
;
if
(
window
.
matchMedia
(
'(prefers-reduced-motion: reduce)'
).
matches
)
{
document
.
querySelectorAll
(
'[data-reveal]'
).
forEach
(
el
=>
{
el
.
style
.
opacity
=
'1'
;
});
return
;
}
const
observer
=
new
IntersectionObserver
(
(
entries
)
=>
{
...
...
@@ -52,21 +62,127 @@ function initReveal() {
}
});
},
{
threshold
:
0.
1
,
rootMargin
:
'0px 0px -5
0px 0px'
}
{
threshold
:
0.
08
,
rootMargin
:
'0px 0px -6
0px 0px'
}
);
document
.
querySelectorAll
(
'[data-reveal]'
).
forEach
((
el
)
=>
{
document
.
querySelectorAll
(
'[data-reveal]'
).
forEach
((
el
,
i
)
=>
{
const
delay
=
parseInt
(
el
.
dataset
.
reveal
)
||
0
;
el
.
style
.
opacity
=
'0'
;
el
.
style
.
transform
=
'translateY(24px)'
;
el
.
style
.
transition
=
`opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1)`
;
const
delay
=
el
.
dataset
.
reveal
||
'0'
;
el
.
style
.
transitionDelay
=
`
${
delay
}
ms`
;
el
.
style
.
transform
=
'translateY(32px)'
;
el
.
style
.
transition
=
`opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1)
${
delay
}
ms, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1)
${
delay
}
ms`
;
observer
.
observe
(
el
);
});
}
document
.
documentElement
.
addEventListener
(
'animationend'
,
()
=>
{});
function
initParallax
()
{
if
(
window
.
matchMedia
(
'(prefers-reduced-motion: reduce)'
).
matches
)
return
;
const
heroVisual
=
document
.
querySelector
(
'.hero__visual'
);
if
(
!
heroVisual
)
return
;
let
ticking
=
false
;
window
.
addEventListener
(
'scroll'
,
()
=>
{
if
(
!
ticking
)
{
requestAnimationFrame
(()
=>
{
const
scrolled
=
window
.
scrollY
;
const
rate
=
scrolled
*
0.15
;
if
(
scrolled
<
window
.
innerHeight
)
{
heroVisual
.
style
.
transform
=
`translateY(
${
rate
}
px)`
;
}
ticking
=
false
;
});
ticking
=
true
;
}
},
{
passive
:
true
});
}
function
initCountUp
()
{
const
stats
=
document
.
querySelectorAll
(
'.stat__value'
);
if
(
!
stats
.
length
)
return
;
const
observer
=
new
IntersectionObserver
(
(
entries
)
=>
{
entries
.
forEach
((
entry
)
=>
{
if
(
entry
.
isIntersecting
)
{
animateValue
(
entry
.
target
);
observer
.
unobserve
(
entry
.
target
);
}
});
},
{
threshold
:
0.5
}
);
stats
.
forEach
(
stat
=>
observer
.
observe
(
stat
));
}
function
animateValue
(
el
)
{
if
(
window
.
matchMedia
(
'(prefers-reduced-motion: reduce)'
).
matches
)
return
;
const
text
=
el
.
textContent
.
trim
();
const
hasPlus
=
text
.
startsWith
(
'+'
);
const
hasPercent
=
text
.
endsWith
(
'%'
);
const
numStr
=
text
.
replace
(
/
[^
0-9.
]
/g
,
''
);
const
target
=
parseFloat
(
numStr
);
if
(
isNaN
(
target
))
return
;
const
duration
=
1500
;
const
start
=
performance
.
now
();
function
update
(
now
)
{
const
elapsed
=
now
-
start
;
const
progress
=
Math
.
min
(
elapsed
/
duration
,
1
);
const
eased
=
1
-
Math
.
pow
(
1
-
progress
,
4
);
const
current
=
Math
.
round
(
eased
*
target
);
el
.
textContent
=
(
hasPlus
?
'+'
:
''
)
+
current
+
(
hasPercent
?
'%'
:
''
);
if
(
progress
<
1
)
{
requestAnimationFrame
(
update
);
}
}
el
.
textContent
=
(
hasPlus
?
'+'
:
''
)
+
'0'
+
(
hasPercent
?
'%'
:
''
);
requestAnimationFrame
(
update
);
}
function
initSmoothHover
()
{
const
cards
=
document
.
querySelectorAll
(
'.module-card, .feature-card, .pain-point'
);
cards
.
forEach
(
card
=>
{
card
.
addEventListener
(
'mouseenter'
,
(
e
)
=>
{
const
rect
=
card
.
getBoundingClientRect
();
const
x
=
e
.
clientX
-
rect
.
left
;
const
y
=
e
.
clientY
-
rect
.
top
;
card
.
style
.
setProperty
(
'--mouse-x'
,
`
${
x
}
px`
);
card
.
style
.
setProperty
(
'--mouse-y'
,
`
${
y
}
px`
);
});
card
.
addEventListener
(
'mousemove'
,
(
e
)
=>
{
const
rect
=
card
.
getBoundingClientRect
();
const
x
=
e
.
clientX
-
rect
.
left
;
const
y
=
e
.
clientY
-
rect
.
top
;
card
.
style
.
setProperty
(
'--mouse-x'
,
`
${
x
}
px`
);
card
.
style
.
setProperty
(
'--mouse-y'
,
`
${
y
}
px`
);
});
});
}
// Inject styles
const
style
=
document
.
createElement
(
'style'
);
style
.
textContent
=
`.revealed { opacity: 1 !important; transform: translateY(0) !important; }`
;
style
.
textContent
=
`
.revealed {
opacity: 1 !important;
transform: translateY(0) !important;
}
.module-card::before,
.feature-card::before,
.pain-point::before {
background: radial-gradient(
400px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
oklch(0.550 0.180 353.3 / 0.04),
transparent 60%
) !important;
opacity: 1 !important;
}
`
;
document
.
head
.
appendChild
(
style
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment