Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
T
tran-quang-hung-portfolio
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
HungTQ
tran-quang-hung-portfolio
Commits
d31f77fc
Commit
d31f77fc
authored
Jun 29, 2026
by
HungTQ
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
style(ui): enhance portfolio motion design
parent
d288b585
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
568 additions
and
143 deletions
+568
-143
App.css
src/App.css
+508
-127
App.tsx
src/App.tsx
+60
-16
No files found.
src/App.css
View file @
d31f77fc
:root
{
:root
{
--ink
:
#111827
;
--ink
:
#f6fbff
;
--muted
:
#5b6678
;
--muted
:
#a7b7c8
;
--paper
:
#f5f0e8
;
--dim
:
#6f8194
;
--surface
:
#fffaf1
;
--paper
:
#061018
;
--line
:
#ded2c1
;
--surface
:
rgba
(
12
,
26
,
38
,
0.78
);
--accent
:
#105c6d
;
--surface-strong
:
rgba
(
16
,
35
,
49
,
0.96
);
--accent-strong
:
#0d4855
;
--line
:
rgba
(
139
,
213
,
255
,
0.18
);
--signal
:
#d64d2a
;
--line-strong
:
rgba
(
139
,
213
,
255
,
0.38
);
--night
:
#172025
;
--accent
:
#42d9ff
;
--shadow
:
0
24px
70px
rgba
(
28
,
38
,
43
,
0.16
);
--accent-strong
:
#9df2ff
;
--signal
:
#ff6b45
;
--signal-soft
:
#ffb199
;
--mint
:
#80ffcc
;
--shadow
:
0
34px
110px
rgba
(
0
,
0
,
0
,
0.48
);
}
}
*
{
*
{
...
@@ -22,12 +26,44 @@ html {
...
@@ -22,12 +26,44 @@ html {
body
{
body
{
margin
:
0
;
margin
:
0
;
background
:
background
:
linear-gradient
(
90deg
,
rgba
(
17
,
24
,
39
,
0.05
)
1px
,
transparent
1px
),
linear-gradient
(
135deg
,
rgba
(
66
,
217
,
255
,
0.10
),
transparent
32%
),
linear-gradient
(
rgba
(
17
,
24
,
39
,
0.04
)
1px
,
transparent
1px
),
linear-gradient
(
225deg
,
rgba
(
255
,
107
,
69
,
0.10
),
transparent
34%
),
radial-gradient
(
circle
at
50%
0%
,
rgba
(
128
,
255
,
204
,
0.07
),
transparent
34%
),
var
(
--paper
);
var
(
--paper
);
background-size
:
42px
42px
;
color
:
var
(
--ink
);
color
:
var
(
--ink
);
font-family
:
"Segoe UI"
,
Candara
,
"Trebuchet MS"
,
sans-serif
;
font-family
:
"Segoe UI"
,
Candara
,
"Trebuchet MS"
,
sans-serif
;
overflow-x
:
hidden
;
}
body
::before
{
animation
:
drift
22s
linear
infinite
;
background-image
:
linear-gradient
(
rgba
(
139
,
213
,
255
,
0.05
)
1px
,
transparent
1px
),
linear-gradient
(
90deg
,
rgba
(
139
,
213
,
255
,
0.05
)
1px
,
transparent
1px
);
background-size
:
42px
42px
;
content
:
""
;
inset
:
0
;
pointer-events
:
none
;
position
:
fixed
;
z-index
:
-2
;
}
body
::after
{
background
:
repeating-linear-gradient
(
90deg
,
rgba
(
255
,
255
,
255
,
0.025
)
0
,
rgba
(
255
,
255
,
255
,
0.025
)
1px
,
transparent
1px
,
transparent
4px
);
content
:
""
;
inset
:
0
;
mix-blend-mode
:
overlay
;
opacity
:
0.55
;
pointer-events
:
none
;
position
:
fixed
;
z-index
:
-1
;
}
}
a
{
a
{
...
@@ -41,17 +77,28 @@ input {
...
@@ -41,17 +77,28 @@ input {
main
{
main
{
min-height
:
100vh
;
min-height
:
100vh
;
position
:
relative
;
}
.ambient-grid
{
background
:
linear-gradient
(
115deg
,
transparent
0
38%
,
rgba
(
66
,
217
,
255
,
0.10
)
38%
39%
,
transparent
39%
100%
),
linear-gradient
(
65deg
,
transparent
0
60%
,
rgba
(
255
,
107
,
69
,
0.10
)
60%
61%
,
transparent
61%
100%
);
inset
:
0
;
pointer-events
:
none
;
position
:
fixed
;
z-index
:
-1
;
}
}
.topbar
{
.topbar
{
align-items
:
center
;
align-items
:
center
;
backdrop-filter
:
blur
(
18
px
);
backdrop-filter
:
blur
(
22
px
);
background
:
rgba
(
245
,
240
,
232
,
0.8
4
);
background
:
rgba
(
6
,
16
,
24
,
0.7
4
);
border-bottom
:
1px
solid
rgba
(
222
,
210
,
193
,
0.8
);
border-bottom
:
1px
solid
var
(
--line
);
display
:
flex
;
display
:
flex
;
justify-content
:
space-between
;
justify-content
:
space-between
;
left
:
0
;
left
:
0
;
padding
:
1
8px
clamp
(
18px
,
5vw
,
64
px
);
padding
:
1
6px
clamp
(
18px
,
5vw
,
72
px
);
position
:
sticky
;
position
:
sticky
;
right
:
0
;
right
:
0
;
top
:
0
;
top
:
0
;
...
@@ -60,54 +107,73 @@ main {
...
@@ -60,54 +107,73 @@ main {
.brand
{
.brand
{
align-items
:
center
;
align-items
:
center
;
background
:
var
(
--night
);
background
:
linear-gradient
(
135deg
,
var
(
--accent
),
var
(
--mint
)
);
border-radius
:
8px
;
border-radius
:
8px
;
color
:
#
fffaf1
;
color
:
#
061018
;
display
:
inline-flex
;
display
:
inline-flex
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
height
:
4
0
px
;
height
:
4
2
px
;
justify-content
:
center
;
justify-content
:
center
;
letter-spacing
:
0.08em
;
letter-spacing
:
0.08em
;
text-decoration
:
none
;
text-decoration
:
none
;
width
:
48
px
;
width
:
52
px
;
}
}
.nav-links
{
.nav-links
{
display
:
flex
;
display
:
flex
;
gap
:
22
px
;
gap
:
8
px
;
}
}
.nav-links
a
{
.nav-links
a
{
border
:
1px
solid
transparent
;
border-radius
:
8px
;
color
:
var
(
--muted
);
color
:
var
(
--muted
);
font-size
:
14px
;
font-size
:
13px
;
font-weight
:
800
;
font-weight
:
850
;
padding
:
9px
12px
;
text-decoration
:
none
;
text-decoration
:
none
;
transition
:
border-color
180ms
ease
,
color
180ms
ease
,
background
180ms
ease
;
}
}
.nav-links
a
:hover
{
.nav-links
a
:hover
{
background
:
rgba
(
139
,
213
,
255
,
0.08
);
border-color
:
var
(
--line
);
color
:
var
(
--ink
);
color
:
var
(
--ink
);
}
}
.hero
{
.hero
{
display
:
grid
;
display
:
grid
;
gap
:
clamp
(
28px
,
5vw
,
72px
);
gap
:
clamp
(
30px
,
5vw
,
86px
);
grid-template-columns
:
minmax
(
0
,
1.04
fr
)
minmax
(
320px
,
0.8
fr
);
grid-template-columns
:
minmax
(
0
,
1.02
fr
)
minmax
(
340px
,
0.82
fr
);
min-height
:
calc
(
100vh
-
77px
);
min-height
:
calc
(
100vh
-
75px
);
padding
:
clamp
(
48px
,
7vw
,
92px
)
clamp
(
18px
,
5vw
,
64px
)
56px
;
overflow
:
hidden
;
padding
:
clamp
(
58px
,
8vw
,
110px
)
clamp
(
18px
,
5vw
,
72px
)
68px
;
position
:
relative
;
}
.hero
::before
{
animation
:
sweep
8s
ease-in-out
infinite
;
background
:
linear-gradient
(
90deg
,
transparent
,
rgba
(
66
,
217
,
255
,
0.16
),
transparent
);
content
:
""
;
height
:
1px
;
left
:
-20%
;
position
:
absolute
;
top
:
17%
;
width
:
140%
;
}
}
.hero-copy
{
.hero-copy
{
align-self
:
center
;
align-self
:
center
;
max-width
:
7
6
0px
;
max-width
:
7
9
0px
;
}
}
.eyebrow
,
.eyebrow
,
.section-kicker
{
.section-kicker
{
color
:
var
(
--accent
);
color
:
var
(
--accent
);
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
letter-spacing
:
0.1
2
em
;
letter-spacing
:
0.1
4
em
;
margin
:
0
0
1
4
px
;
margin
:
0
0
1
6
px
;
text-transform
:
uppercase
;
text-transform
:
uppercase
;
}
}
...
@@ -120,16 +186,30 @@ p {
...
@@ -120,16 +186,30 @@ p {
h1
{
h1
{
font-family
:
Georgia
,
"Times New Roman"
,
serif
;
font-family
:
Georgia
,
"Times New Roman"
,
serif
;
font-size
:
clamp
(
56px
,
10vw
,
124px
);
font-size
:
clamp
(
58px
,
10vw
,
132px
);
line-height
:
0.88
;
line-height
:
0.82
;
margin-bottom
:
20px
;
margin-bottom
:
24px
;
max-width
:
900px
;
max-width
:
980px
;
}
h1
span
{
animation
:
titleRise
780ms
cubic-bezier
(
0.2
,
0.8
,
0.2
,
1
)
both
;
display
:
block
;
text-shadow
:
0
0
34px
rgba
(
66
,
217
,
255
,
0.18
);
}
h1
span
:nth-child
(
2
)
{
animation-delay
:
80ms
;
}
h1
span
:nth-child
(
3
)
{
animation-delay
:
160ms
;
}
}
h2
{
h2
{
font-family
:
Georgia
,
"Times New Roman"
,
serif
;
font-family
:
Georgia
,
"Times New Roman"
,
serif
;
font-size
:
clamp
(
34px
,
5vw
,
6
4
px
);
font-size
:
clamp
(
34px
,
5vw
,
6
8
px
);
line-height
:
1
;
line-height
:
0.98
;
margin-bottom
:
0
;
margin-bottom
:
0
;
}
}
...
@@ -140,24 +220,24 @@ h3 {
...
@@ -140,24 +220,24 @@ h3 {
}
}
.role
{
.role
{
color
:
var
(
--signal
);
color
:
var
(
--signal
-soft
);
font-size
:
clamp
(
22px
,
3vw
,
3
2
px
);
font-size
:
clamp
(
22px
,
3vw
,
3
4
px
);
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
margin-bottom
:
18px
;
margin-bottom
:
18px
;
}
}
.summary
{
.summary
{
color
:
var
(
--muted
);
color
:
var
(
--muted
);
font-size
:
clamp
(
17px
,
2vw
,
22px
);
font-size
:
clamp
(
17px
,
2vw
,
22px
);
line-height
:
1.5
5
;
line-height
:
1.5
8
;
max-width
:
7
2
0px
;
max-width
:
7
6
0px
;
}
}
.hero-actions
{
.hero-actions
{
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
12px
;
gap
:
12px
;
margin
:
3
2
px
0
;
margin
:
3
4
px
0
;
}
}
.primary-action
,
.primary-action
,
...
@@ -165,29 +245,36 @@ h3 {
...
@@ -165,29 +245,36 @@ h3 {
align-items
:
center
;
align-items
:
center
;
border-radius
:
8px
;
border-radius
:
8px
;
display
:
inline-flex
;
display
:
inline-flex
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
gap
:
8px
;
gap
:
8px
;
min-height
:
48
px
;
min-height
:
50
px
;
padding
:
0
18px
;
padding
:
0
18px
;
text-decoration
:
none
;
text-decoration
:
none
;
transform
:
translateZ
(
0
);
transition
:
transform
180ms
ease
,
box-shadow
180ms
ease
,
border-color
180ms
ease
;
}
}
.primary-action
{
.primary-action
{
background
:
var
(
--accent
);
background
:
linear-gradient
(
135deg
,
var
(
--accent
),
var
(
--mint
));
color
:
#fffaf1
;
box-shadow
:
0
18px
46px
rgba
(
66
,
217
,
255
,
0.22
);
color
:
#061018
;
}
}
.primary-action
:hover
{
.primary-action
:hover
,
background
:
var
(
--accent-strong
);
.secondary-action
:hover
{
transform
:
translateY
(
-3px
);
}
}
.secondary-action
{
.secondary-action
{
background
:
rgba
(
255
,
250
,
241
,
0.
8
);
background
:
rgba
(
12
,
26
,
38
,
0.7
8
);
border
:
1px
solid
var
(
--line
);
border
:
1px
solid
var
(
--line
);
color
:
var
(
--ink
);
color
:
var
(
--ink
);
}
}
.signal-row
{
.signal-row
,
.skill-cloud
,
.stack-list
,
.project-links
{
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
gap
:
10px
;
gap
:
10px
;
...
@@ -196,38 +283,85 @@ h3 {
...
@@ -196,38 +283,85 @@ h3 {
.signal-row
span
,
.signal-row
span
,
.skill-cloud
span
,
.skill-cloud
span
,
.stack-list
span
{
.stack-list
span
{
background
:
#fffaf1
;
background
:
rgba
(
12
,
26
,
38
,
0.82
)
;
border
:
1px
solid
var
(
--line
);
border
:
1px
solid
var
(
--line
);
border-radius
:
8px
;
border-radius
:
8px
;
color
:
var
(
--accent-strong
);
color
:
var
(
--accent-strong
);
display
:
inline-flex
;
display
:
inline-flex
;
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
padding
:
8px
10px
;
padding
:
8px
10px
;
}
}
.hero-metrics
{
display
:
grid
;
gap
:
10px
;
grid-template-columns
:
repeat
(
3
,
minmax
(
0
,
1
fr
));
margin-top
:
28px
;
max-width
:
650px
;
}
.hero-metrics
div
{
background
:
rgba
(
12
,
26
,
38
,
0.66
);
border
:
1px
solid
var
(
--line
);
border-radius
:
8px
;
padding
:
14px
;
}
.hero-metrics
strong
,
.hero-metrics
span
{
display
:
block
;
}
.hero-metrics
strong
{
color
:
var
(
--ink
);
font-size
:
22px
;
margin-bottom
:
5px
;
}
.hero-metrics
span
{
color
:
var
(
--dim
);
font-size
:
12px
;
font-weight
:
850
;
text-transform
:
uppercase
;
}
.hero-visual
{
.hero-visual
{
align-items
:
center
;
align-items
:
center
;
display
:
flex
;
display
:
flex
;
justify-content
:
center
;
justify-content
:
center
;
min-height
:
5
2
0px
;
min-height
:
5
6
0px
;
position
:
relative
;
position
:
relative
;
}
}
.device-halo
{
animation
:
pulseGlow
4.6s
ease-in-out
infinite
;
background
:
conic-gradient
(
from
120deg
,
rgba
(
66
,
217
,
255
,
0
),
rgba
(
66
,
217
,
255
,
0.55
),
rgba
(
255
,
107
,
69
,
0.45
),
rgba
(
66
,
217
,
255
,
0
));
border-radius
:
40px
;
filter
:
blur
(
30px
);
height
:
78%
;
opacity
:
0.62
;
position
:
absolute
;
transform
:
rotate
(
8deg
);
width
:
min
(
88%
,
440px
);
}
.phone-frame
{
.phone-frame
{
background
:
#1b252b
;
animation
:
deviceFloat
5.5s
ease-in-out
infinite
;
border
:
12px
solid
#1b252b
;
background
:
#0a151e
;
border-radius
:
30px
;
border
:
12px
solid
#111f2a
;
box-shadow
:
var
(
--shadow
);
border-radius
:
32px
;
max-width
:
360px
;
box-shadow
:
var
(
--shadow
),
inset
0
0
0
1px
rgba
(
255
,
255
,
255
,
0.06
);
max-width
:
365px
;
overflow
:
hidden
;
overflow
:
hidden
;
position
:
relative
;
position
:
relative
;
transform
:
rotate
(
2deg
);
transform
:
rotate
(
2deg
);
width
:
min
(
100%
,
36
0
px
);
width
:
min
(
100%
,
36
5
px
);
}
}
.phone-speaker
{
.phone-speaker
{
background
:
#
303c42
;
background
:
#
2c3c47
;
border-radius
:
999px
;
border-radius
:
999px
;
height
:
6px
;
height
:
6px
;
left
:
50%
;
left
:
50%
;
...
@@ -235,58 +369,78 @@ h3 {
...
@@ -235,58 +369,78 @@ h3 {
top
:
12px
;
top
:
12px
;
transform
:
translateX
(
-50%
);
transform
:
translateX
(
-50%
);
width
:
76px
;
width
:
76px
;
z-index
:
3
;
}
.screen-scan
{
animation
:
scan
3.6s
ease-in-out
infinite
;
background
:
linear-gradient
(
180deg
,
transparent
,
rgba
(
66
,
217
,
255
,
0.30
),
transparent
);
height
:
24%
;
left
:
0
;
pointer-events
:
none
;
position
:
absolute
;
right
:
0
;
top
:
-30%
;
z-index
:
2
;
z-index
:
2
;
}
}
.phone-frame
img
{
.phone-frame
img
{
display
:
block
;
display
:
block
;
filter
:
saturate
(
0.92
)
contrast
(
1.04
);
filter
:
saturate
(
1.02
)
contrast
(
1.07
);
width
:
100%
;
width
:
100%
;
}
}
.floating-note
{
.floating-note
{
align-items
:
center
;
align-items
:
center
;
background
:
var
(
--signal
);
animation
:
noteBob
4.2s
ease-in-out
infinite
;
background
:
linear-gradient
(
135deg
,
var
(
--signal
),
#ff9a62
);
border-radius
:
8px
;
border-radius
:
8px
;
bottom
:
46
px
;
bottom
:
54
px
;
box-shadow
:
var
(
--shadow
);
box-shadow
:
0
22px
50px
rgba
(
255
,
107
,
69
,
0.26
);
color
:
#fff
af1
;
color
:
#fff
;
display
:
flex
;
display
:
flex
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
gap
:
10px
;
gap
:
10px
;
padding
:
14px
16px
;
padding
:
14px
16px
;
position
:
absolute
;
position
:
absolute
;
right
:
4
%
;
right
:
2
%
;
}
}
.section
{
.section
{
padding
:
clamp
(
52px
,
7vw
,
92px
)
clamp
(
18px
,
5vw
,
64px
);
padding
:
clamp
(
56px
,
7vw
,
98px
)
clamp
(
18px
,
5vw
,
72px
);
position
:
relative
;
}
}
.intro-grid
{
.intro-grid
{
align-items
:
end
;
align-items
:
end
;
background
:
var
(
--night
);
background
:
color
:
#fffaf1
;
linear-gradient
(
135deg
,
rgba
(
66
,
217
,
255
,
0.13
),
transparent
38%
),
#09151e
;
border-block
:
1px
solid
var
(
--line
);
display
:
grid
;
display
:
grid
;
gap
:
32px
;
gap
:
32px
;
grid-template-columns
:
minmax
(
0
,
0.95
fr
)
minmax
(
280px
,
0.8
fr
);
grid-template-columns
:
minmax
(
0
,
0.95
fr
)
minmax
(
280px
,
0.8
fr
);
}
}
.intro-grid
.section-kicker
{
.intro-grid
.section-kicker
{
color
:
#f2b39f
;
color
:
var
(
--mint
)
;
}
}
.intro-grid
p
:last-child
{
.intro-grid
p
:last-child
{
color
:
#c8d2d3
;
color
:
var
(
--muted
)
;
font-size
:
19px
;
font-size
:
19px
;
line-height
:
1.6
;
line-height
:
1.6
5
;
}
}
.section-heading
{
.section-heading
{
align-items
:
end
;
align-items
:
end
;
display
:
flex
;
display
:
flex
;
justify-content
:
space-between
;
justify-content
:
space-between
;
margin-bottom
:
28px
;
margin-bottom
:
32px
;
}
.projects-section
{
background
:
linear-gradient
(
180deg
,
rgba
(
11
,
28
,
40
,
0.82
),
rgba
(
6
,
16
,
24
,
0.94
));
}
}
.project-grid
{
.project-grid
{
...
@@ -299,59 +453,98 @@ h3 {
...
@@ -299,59 +453,98 @@ h3 {
.assistant-box
,
.assistant-box
,
.skill-group
,
.skill-group
,
.contact-list
{
.contact-list
{
background
:
rgba
(
255
,
250
,
241
,
0.9
);
background
:
var
(
--surface
);
border
:
1px
solid
var
(
--line
);
border
:
1px
solid
var
(
--line
);
border-radius
:
8px
;
border-radius
:
8px
;
box-shadow
:
0
1
2px
34px
rgba
(
28
,
38
,
43
,
0.08
);
box-shadow
:
0
1
8px
55px
rgba
(
0
,
0
,
0
,
0.22
);
}
}
.project-card
{
.project-card
{
padding
:
clamp
(
22px
,
3vw
,
32px
);
min-height
:
330px
;
overflow
:
hidden
;
padding
:
clamp
(
22px
,
3vw
,
34px
);
position
:
relative
;
transition
:
border-color
220ms
ease
,
transform
220ms
ease
,
box-shadow
220ms
ease
;
}
.project-card
::before
{
background
:
linear-gradient
(
135deg
,
rgba
(
66
,
217
,
255
,
0.18
),
transparent
45%
);
content
:
""
;
inset
:
0
;
opacity
:
0
;
position
:
absolute
;
transition
:
opacity
220ms
ease
;
}
.project-card
:hover
{
border-color
:
var
(
--line-strong
);
box-shadow
:
0
26px
80px
rgba
(
66
,
217
,
255
,
0.13
);
transform
:
translateY
(
-6px
);
}
.project-card
:hover::before
{
opacity
:
1
;
}
.project-card
>
*
{
position
:
relative
;
z-index
:
1
;
}
.project-index
{
color
:
rgba
(
157
,
242
,
255
,
0.16
);
font-family
:
Georgia
,
"Times New Roman"
,
serif
;
font-size
:
106px
;
font-weight
:
900
;
line-height
:
0.8
;
position
:
absolute
;
right
:
24px
;
top
:
22px
;
}
}
.project-meta
{
.project-meta
{
color
:
var
(
--
muted
);
color
:
var
(
--
dim
);
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
gap
:
10px
;
gap
:
10px
;
justify-content
:
space-between
;
justify-content
:
space-between
;
margin-bottom
:
18
px
;
margin-bottom
:
20
px
;
text-transform
:
uppercase
;
text-transform
:
uppercase
;
}
}
.project-card
p
{
.project-card
p
{
color
:
var
(
--muted
);
color
:
var
(
--muted
);
line-height
:
1.
58
;
line-height
:
1.
62
;
}
}
.stack-list
,
.stack-list
,
.project-links
{
.project-links
{
display
:
flex
;
margin-top
:
20px
;
flex-wrap
:
wrap
;
gap
:
8px
;
margin-top
:
18px
;
}
}
.project-links
a
{
.project-links
a
{
align-items
:
center
;
align-items
:
center
;
color
:
var
(
--signal
);
color
:
var
(
--signal
-soft
);
display
:
inline-flex
;
display
:
inline-flex
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
gap
:
4px
;
gap
:
4px
;
text-decoration
:
none
;
text-decoration
:
none
;
}
}
.project-links
a
:hover
{
color
:
#fff
;
}
.skills-section
{
.skills-section
{
background
:
#f0e3d3
;
background
:
linear-gradient
(
90deg
,
rgba
(
128
,
255
,
204
,
0.08
),
transparent
40%
),
#07111a
;
}
}
.skill-cloud
{
.skill-cloud
{
display
:
flex
;
margin-bottom
:
30px
;
flex-wrap
:
wrap
;
gap
:
10px
;
margin-bottom
:
28px
;
}
}
.skill-groups
{
.skill-groups
{
...
@@ -361,16 +554,23 @@ h3 {
...
@@ -361,16 +554,23 @@ h3 {
}
}
.skill-group
{
.skill-group
{
padding
:
20px
;
padding
:
22px
;
transition
:
transform
200ms
ease
,
border-color
200ms
ease
;
}
.skill-group
:hover
{
border-color
:
var
(
--line-strong
);
transform
:
translateY
(
-4px
);
}
}
.skill-group
h3
{
.skill-group
h3
{
color
:
var
(
--accent-strong
);
font-size
:
18px
;
font-size
:
18px
;
}
}
.skill-group
ul
{
.skill-group
ul
{
color
:
var
(
--muted
);
color
:
var
(
--muted
);
line-height
:
1.8
;
line-height
:
1.8
5
;
list-style
:
none
;
list-style
:
none
;
margin
:
0
;
margin
:
0
;
padding
:
0
;
padding
:
0
;
...
@@ -378,20 +578,36 @@ h3 {
...
@@ -378,20 +578,36 @@ h3 {
.ai-panel
{
.ai-panel
{
align-items
:
start
;
align-items
:
start
;
background
:
linear-gradient
(
135deg
,
rgba
(
255
,
107
,
69
,
0.10
),
transparent
35%
),
#061018
;
display
:
grid
;
display
:
grid
;
gap
:
clamp
(
24px
,
5vw
,
72px
);
gap
:
clamp
(
24px
,
5vw
,
72px
);
grid-template-columns
:
minmax
(
0
,
0.
8
fr
)
minmax
(
32
0px
,
1
fr
);
grid-template-columns
:
minmax
(
0
,
0.
78
fr
)
minmax
(
33
0px
,
1
fr
);
}
}
.ai-copy
p
:last-child
{
.ai-copy
p
:last-child
{
color
:
var
(
--muted
);
color
:
var
(
--muted
);
font-size
:
18px
;
font-size
:
18px
;
line-height
:
1.6
;
line-height
:
1.6
4
;
margin-top
:
18px
;
margin-top
:
18px
;
}
}
.assistant-box
{
.assistant-box
{
overflow
:
hidden
;
padding
:
clamp
(
20px
,
4vw
,
32px
);
padding
:
clamp
(
20px
,
4vw
,
32px
);
position
:
relative
;
}
.assistant-box
::before
{
animation
:
assistantRail
5s
linear
infinite
;
background
:
linear-gradient
(
90deg
,
transparent
,
rgba
(
128
,
255
,
204
,
0.34
),
transparent
);
content
:
""
;
height
:
1px
;
left
:
-40%
;
position
:
absolute
;
top
:
0
;
width
:
80%
;
}
}
.assistant-header
{
.assistant-header
{
...
@@ -399,9 +615,18 @@ h3 {
...
@@ -399,9 +615,18 @@ h3 {
color
:
var
(
--accent-strong
);
color
:
var
(
--accent-strong
);
display
:
flex
;
display
:
flex
;
font-size
:
18px
;
font-size
:
18px
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
gap
:
10px
;
gap
:
10px
;
margin-bottom
:
18px
;
margin-bottom
:
20px
;
}
.assistant-header
i
{
animation
:
blink
1.3s
steps
(
2
,
end
)
infinite
;
background
:
var
(
--mint
);
border-radius
:
999px
;
height
:
8px
;
margin-left
:
auto
;
width
:
8px
;
}
}
.quick-questions
{
.quick-questions
{
...
@@ -412,21 +637,27 @@ h3 {
...
@@ -412,21 +637,27 @@ h3 {
}
}
.quick-questions
button
{
.quick-questions
button
{
background
:
#f0e3d3
;
background
:
rgba
(
66
,
217
,
255
,
0.08
)
;
border
:
1px
solid
var
(
--line
);
border
:
1px
solid
var
(
--line
);
border-radius
:
8px
;
border-radius
:
8px
;
color
:
var
(
--ink
);
color
:
var
(
--ink
);
cursor
:
pointer
;
cursor
:
pointer
;
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
8
0
0
;
font-weight
:
8
5
0
;
padding
:
8px
10px
;
padding
:
8px
10px
;
transition
:
background
180ms
ease
,
transform
180ms
ease
;
}
.quick-questions
button
:hover
{
background
:
rgba
(
66
,
217
,
255
,
0.16
);
transform
:
translateY
(
-2px
);
}
}
.input-label
{
.input-label
{
color
:
var
(
--
muted
);
color
:
var
(
--
dim
);
display
:
block
;
display
:
block
;
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
margin-bottom
:
8px
;
margin-bottom
:
8px
;
text-transform
:
uppercase
;
text-transform
:
uppercase
;
}
}
...
@@ -434,72 +665,79 @@ h3 {
...
@@ -434,72 +665,79 @@ h3 {
.ask-row
{
.ask-row
{
display
:
grid
;
display
:
grid
;
gap
:
8px
;
gap
:
8px
;
grid-template-columns
:
minmax
(
0
,
1
fr
)
48
px
;
grid-template-columns
:
minmax
(
0
,
1
fr
)
50
px
;
}
}
.ask-row
input
{
.ask-row
input
{
background
:
#fffdf8
;
background
:
rgba
(
255
,
255
,
255
,
0.05
)
;
border
:
1px
solid
var
(
--line
);
border
:
1px
solid
var
(
--line
);
border-radius
:
8px
;
border-radius
:
8px
;
color
:
var
(
--ink
);
color
:
var
(
--ink
);
min-height
:
48
px
;
min-height
:
50
px
;
padding
:
0
12px
;
padding
:
0
12px
;
width
:
100%
;
width
:
100%
;
}
}
.ask-row
input
:focus
{
border-color
:
var
(
--accent
);
outline
:
none
;
}
.ask-row
button
{
.ask-row
button
{
align-items
:
center
;
align-items
:
center
;
background
:
var
(
--accent
);
background
:
linear-gradient
(
135deg
,
var
(
--accent
),
var
(
--mint
)
);
border
:
0
;
border
:
0
;
border-radius
:
8px
;
border-radius
:
8px
;
color
:
#
fffaf1
;
color
:
#
061018
;
cursor
:
pointer
;
cursor
:
pointer
;
display
:
inline-flex
;
display
:
inline-flex
;
justify-content
:
center
;
justify-content
:
center
;
}
}
.answer-box
{
.answer-box
{
background
:
var
(
--night
);
animation
:
answerIn
260ms
ease
both
;
background
:
#02080d
;
border
:
1px
solid
rgba
(
128
,
255
,
204
,
0.20
);
border-radius
:
8px
;
border-radius
:
8px
;
color
:
#fff
af1
;
color
:
#fff
;
margin-top
:
16px
;
margin-top
:
16px
;
padding
:
18px
;
padding
:
18px
;
}
}
.answer-box
span
{
.answer-box
span
{
color
:
#f2b39f
;
color
:
var
(
--mint
)
;
display
:
block
;
display
:
block
;
font-size
:
13px
;
font-size
:
13px
;
font-weight
:
9
0
0
;
font-weight
:
9
5
0
;
margin-bottom
:
8px
;
margin-bottom
:
8px
;
text-transform
:
uppercase
;
text-transform
:
uppercase
;
}
}
.answer-box
p
{
.answer-box
p
{
color
:
#dce
4e5
;
color
:
#dce
7ef
;
line-height
:
1.5
5
;
line-height
:
1.5
8
;
margin
:
0
;
margin
:
0
;
}
}
.contact-section
{
.contact-section
{
align-items
:
start
;
align-items
:
start
;
background
:
var
(
--night
)
;
background
:
#02080d
;
color
:
#fffaf1
;
border-top
:
1px
solid
var
(
--line
)
;
display
:
grid
;
display
:
grid
;
gap
:
32px
;
gap
:
32px
;
grid-template-columns
:
minmax
(
0
,
0.95
fr
)
minmax
(
320px
,
0.8
fr
);
grid-template-columns
:
minmax
(
0
,
0.95
fr
)
minmax
(
320px
,
0.8
fr
);
}
}
.contact-section
.section-kicker
{
.contact-section
.section-kicker
{
color
:
#f2b39f
;
color
:
var
(
--signal-soft
)
;
}
}
.contact-list
{
.contact-list
{
background
:
rgba
(
255
,
25
0
,
241
,
0.08
);
background
:
rgba
(
255
,
25
5
,
255
,
0.05
);
border-color
:
rgba
(
255
,
25
0
,
241
,
0.15
);
border-color
:
rgba
(
255
,
25
5
,
255
,
0.12
);
display
:
grid
;
display
:
grid
;
gap
:
1
0
px
;
gap
:
1
2
px
;
padding
:
2
0
px
;
padding
:
2
2
px
;
}
}
.contact-list
a
,
.contact-list
a
,
...
@@ -512,20 +750,145 @@ footer span {
...
@@ -512,20 +750,145 @@ footer span {
.contact-list
a
,
.contact-list
a
,
.contact-list
span
{
.contact-list
span
{
color
:
#dce
4e5
;
color
:
#dce
7ef
;
text-decoration
:
none
;
text-decoration
:
none
;
}
}
.contact-list
a
:hover
{
color
:
var
(
--accent-strong
);
}
footer
{
footer
{
align-items
:
center
;
align-items
:
center
;
background
:
#0d1317
;
background
:
#02080d
;
color
:
#bac5c7
;
border-top
:
1px
solid
rgba
(
255
,
255
,
255
,
0.08
);
color
:
var
(
--dim
);
display
:
flex
;
display
:
flex
;
flex-wrap
:
wrap
;
flex-wrap
:
wrap
;
font-size
:
14px
;
font-size
:
14px
;
gap
:
12px
;
gap
:
12px
;
justify-content
:
space-between
;
justify-content
:
space-between
;
padding
:
18px
clamp
(
18px
,
5vw
,
64px
);
padding
:
18px
clamp
(
18px
,
5vw
,
72px
);
}
.reveal
{
animation
:
revealIn
760ms
cubic-bezier
(
0.2
,
0.8
,
0.2
,
1
)
var
(
--delay
,
0ms
)
both
;
}
.reveal.is-visible
{
opacity
:
1
;
transform
:
translateY
(
0
);
}
@keyframes
titleRise
{
from
{
opacity
:
0
;
transform
:
translateY
(
32px
);
}
to
{
opacity
:
1
;
transform
:
translateY
(
0
);
}
}
@keyframes
drift
{
from
{
background-position
:
0
0
;
}
to
{
background-position
:
42px
42px
;
}
}
@keyframes
sweep
{
0
%,
100
%
{
opacity
:
0.05
;
transform
:
translateX
(
-12%
);
}
50
%
{
opacity
:
0.7
;
transform
:
translateX
(
12%
);
}
}
@keyframes
deviceFloat
{
0
%,
100
%
{
transform
:
rotate
(
2deg
)
translateY
(
0
);
}
50
%
{
transform
:
rotate
(
-1deg
)
translateY
(
-14px
);
}
}
@keyframes
pulseGlow
{
0
%,
100
%
{
opacity
:
0.44
;
transform
:
rotate
(
8deg
)
scale
(
0.96
);
}
50
%
{
opacity
:
0.76
;
transform
:
rotate
(
14deg
)
scale
(
1.04
);
}
}
@keyframes
scan
{
0
%
{
top
:
-30%
;
}
55
%,
100
%
{
top
:
112%
;
}
}
@keyframes
noteBob
{
0
%,
100
%
{
transform
:
translateY
(
0
);
}
50
%
{
transform
:
translateY
(
-9px
);
}
}
@keyframes
assistantRail
{
from
{
transform
:
translateX
(
0
);
}
to
{
transform
:
translateX
(
180%
);
}
}
@keyframes
blink
{
50
%
{
opacity
:
0.25
;
}
}
@keyframes
answerIn
{
from
{
opacity
:
0
;
transform
:
translateY
(
8px
);
}
to
{
opacity
:
1
;
transform
:
translateY
(
0
);
}
}
@keyframes
revealIn
{
from
{
opacity
:
0
;
transform
:
translateY
(
26px
);
}
to
{
opacity
:
1
;
transform
:
translateY
(
0
);
}
}
}
@media
(
max-width
:
900px
)
{
@media
(
max-width
:
900px
)
{
...
@@ -548,6 +911,7 @@ footer {
...
@@ -548,6 +911,7 @@ footer {
min-height
:
auto
;
min-height
:
auto
;
}
}
.hero-metrics
,
.project-grid
,
.project-grid
,
.skill-groups
{
.skill-groups
{
grid-template-columns
:
1
fr
;
grid-template-columns
:
1
fr
;
...
@@ -560,6 +924,23 @@ footer {
...
@@ -560,6 +924,23 @@ footer {
.floating-note
{
.floating-note
{
bottom
:
18px
;
bottom
:
18px
;
max-width
:
calc
(
100%
-
20px
);
right
:
0
;
right
:
0
;
}
}
}
}
@media
(
prefers-reduced-motion
:
reduce
)
{
*,
*
::before
,
*
::after
{
animation
:
none
!important
;
scroll-behavior
:
auto
!important
;
transition
:
none
!important
;
}
.reveal
{
animation
:
none
!important
;
opacity
:
1
;
transform
:
none
;
}
}
src/App.tsx
View file @
d31f77fc
import
{
use
Memo
,
useState
}
from
'react'
import
{
use
Effect
,
useMemo
,
useState
,
type
CSSProperties
}
from
'react'
import
{
import
{
ArrowUpRight
,
ArrowUpRight
,
Bot
,
Bot
,
...
@@ -72,6 +72,12 @@ const quickQuestions = [
...
@@ -72,6 +72,12 @@ const quickQuestions = [
'How can I contact Hung?'
,
'How can I contact Hung?'
,
]
]
const
metrics
=
[
{
label
:
'Mobile projects'
,
value
:
'02'
},
{
label
:
'Core stack'
,
value
:
'RN + Flutter'
},
{
label
:
'Graduation'
,
value
:
'2027'
},
]
function
answerQuestion
(
question
:
string
)
{
function
answerQuestion
(
question
:
string
)
{
const
normalized
=
question
.
toLowerCase
()
const
normalized
=
question
.
toLowerCase
()
...
@@ -102,21 +108,42 @@ function answerQuestion(question: string) {
...
@@ -102,21 +108,42 @@ function answerQuestion(question: string) {
function
App
()
{
function
App
()
{
const
[
question
,
setQuestion
]
=
useState
(
quickQuestions
[
0
])
const
[
question
,
setQuestion
]
=
useState
(
quickQuestions
[
0
])
const
[
answer
,
setAnswer
]
=
useState
(
answerQuestion
(
quickQuestions
[
0
]))
const
[
answer
,
setAnswer
]
=
useState
(
answerQuestion
(
quickQuestions
[
0
]))
const
[
answerPulse
,
setAnswerPulse
]
=
useState
(
0
)
const
featuredSkills
=
useMemo
(
const
featuredSkills
=
useMemo
(
()
=>
skillGroups
.
flatMap
((
group
)
=>
group
.
items
).
slice
(
0
,
9
),
()
=>
skillGroups
.
flatMap
((
group
)
=>
group
.
items
).
slice
(
0
,
9
),
[],
[],
)
)
useEffect
(()
=>
{
const
revealItems
=
document
.
querySelectorAll
<
HTMLElement
>
(
'.reveal'
)
const
observer
=
new
IntersectionObserver
(
(
entries
)
=>
{
entries
.
forEach
((
entry
)
=>
{
if
(
entry
.
isIntersecting
)
{
entry
.
target
.
classList
.
add
(
'is-visible'
)
observer
.
unobserve
(
entry
.
target
)
}
})
},
{
threshold
:
0.18
},
)
revealItems
.
forEach
((
item
)
=>
observer
.
observe
(
item
))
return
()
=>
observer
.
disconnect
()
},
[])
function
handleAsk
(
nextQuestion
=
question
)
{
function
handleAsk
(
nextQuestion
=
question
)
{
const
trimmed
=
nextQuestion
.
trim
()
const
trimmed
=
nextQuestion
.
trim
()
if
(
!
trimmed
)
return
if
(
!
trimmed
)
return
setQuestion
(
trimmed
)
setQuestion
(
trimmed
)
setAnswer
(
answerQuestion
(
trimmed
))
setAnswer
(
answerQuestion
(
trimmed
))
setAnswerPulse
((
current
)
=>
current
+
1
)
}
}
return
(
return
(
<
main
>
<
main
>
<
div
className=
"ambient-grid"
aria
-
hidden=
"true"
/>
<
nav
className=
"topbar"
aria
-
label=
"Primary navigation"
>
<
nav
className=
"topbar"
aria
-
label=
"Primary navigation"
>
<
a
className=
"brand"
href=
"#hero"
>
TQH
</
a
>
<
a
className=
"brand"
href=
"#hero"
>
TQH
</
a
>
<
div
className=
"nav-links"
>
<
div
className=
"nav-links"
>
...
@@ -128,9 +155,13 @@ function App() {
...
@@ -128,9 +155,13 @@ function App() {
</
nav
>
</
nav
>
<
section
className=
"hero"
id=
"hero"
>
<
section
className=
"hero"
id=
"hero"
>
<
div
className=
"hero-copy"
>
<
div
className=
"hero-copy
reveal
"
>
<
p
className=
"eyebrow"
>
Mobile portfolio / React Native intern
</
p
>
<
p
className=
"eyebrow"
>
Mobile portfolio / React Native intern
</
p
>
<
h1
>
{
profile
.
name
}
</
h1
>
<
h1
>
<
span
>
Tran
</
span
>
<
span
>
Quang
</
span
>
<
span
>
Hung
</
span
>
</
h1
>
<
p
className=
"role"
>
{
profile
.
role
}
</
p
>
<
p
className=
"role"
>
{
profile
.
role
}
</
p
>
<
p
className=
"summary"
>
{
profile
.
summary
}
</
p
>
<
p
className=
"summary"
>
{
profile
.
summary
}
</
p
>
...
@@ -149,11 +180,22 @@ function App() {
...
@@ -149,11 +180,22 @@ function App() {
<
span
>
Firebase
</
span
>
<
span
>
Firebase
</
span
>
<
span
>
Figma UI/UX
</
span
>
<
span
>
Figma UI/UX
</
span
>
</
div
>
</
div
>
<
div
className=
"hero-metrics"
aria
-
label=
"Profile metrics"
>
{
metrics
.
map
((
metric
)
=>
(
<
div
key=
{
metric
.
label
}
>
<
strong
>
{
metric
.
value
}
</
strong
>
<
span
>
{
metric
.
label
}
</
span
>
</
div
>
))
}
</
div
>
</
div
>
</
div
>
<
div
className=
"hero-visual"
aria
-
label=
"CV preview"
>
<
div
className=
"hero-visual reveal"
aria
-
label=
"CV preview"
>
<
div
className=
"device-halo"
aria
-
hidden=
"true"
/>
<
div
className=
"phone-frame"
>
<
div
className=
"phone-frame"
>
<
div
className=
"phone-speaker"
/>
<
div
className=
"phone-speaker"
/>
<
div
className=
"screen-scan"
aria
-
hidden=
"true"
/>
<
img
src=
"/cv-preview.png"
alt=
"Tran Quang Hung CV preview"
/>
<
img
src=
"/cv-preview.png"
alt=
"Tran Quang Hung CV preview"
/>
</
div
>
</
div
>
<
div
className=
"floating-note"
>
<
div
className=
"floating-note"
>
...
@@ -163,7 +205,7 @@ function App() {
...
@@ -163,7 +205,7 @@ function App() {
</
div
>
</
div
>
</
section
>
</
section
>
<
section
className=
"section intro-grid"
aria
-
label=
"Profile summary"
>
<
section
className=
"section intro-grid
reveal
"
aria
-
label=
"Profile summary"
>
<
div
>
<
div
>
<
p
className=
"section-kicker"
>
Internship fit
</
p
>
<
p
className=
"section-kicker"
>
Internship fit
</
p
>
<
h2
>
Building mobile interfaces with product-minded UI decisions.
</
h2
>
<
h2
>
Building mobile interfaces with product-minded UI decisions.
</
h2
>
...
@@ -175,15 +217,16 @@ function App() {
...
@@ -175,15 +217,16 @@ function App() {
</
p
>
</
p
>
</
section
>
</
section
>
<
section
className=
"section"
id=
"projects"
>
<
section
className=
"section
projects-section
"
id=
"projects"
>
<
div
className=
"section-heading"
>
<
div
className=
"section-heading
reveal
"
>
<
p
className=
"section-kicker"
>
Selected work
</
p
>
<
p
className=
"section-kicker"
>
Selected work
</
p
>
<
h2
>
Projects from my CV
</
h2
>
<
h2
>
Projects from my CV
</
h2
>
</
div
>
</
div
>
<
div
className=
"project-grid"
>
<
div
className=
"project-grid"
>
{
projects
.
map
((
project
)
=>
(
{
projects
.
map
((
project
,
index
)
=>
(
<
article
className=
"project-card"
key=
{
project
.
name
}
>
<
article
className=
"project-card reveal"
key=
{
project
.
name
}
style=
{
{
'--delay'
:
`${index * 110}ms`
}
as
CSSProperties
}
>
<
div
className=
"project-index"
>
0
{
index
+
1
}
</
div
>
<
div
className=
"project-meta"
>
<
div
className=
"project-meta"
>
<
span
>
{
project
.
type
}
</
span
>
<
span
>
{
project
.
type
}
</
span
>
<
span
>
{
project
.
time
}
</
span
>
<
span
>
{
project
.
time
}
</
span
>
...
@@ -208,7 +251,7 @@ function App() {
...
@@ -208,7 +251,7 @@ function App() {
</
section
>
</
section
>
<
section
className=
"section skills-section"
id=
"skills"
>
<
section
className=
"section skills-section"
id=
"skills"
>
<
div
className=
"section-heading"
>
<
div
className=
"section-heading
reveal
"
>
<
p
className=
"section-kicker"
>
Capability map
</
p
>
<
p
className=
"section-kicker"
>
Capability map
</
p
>
<
h2
>
Technical skills
</
h2
>
<
h2
>
Technical skills
</
h2
>
</
div
>
</
div
>
...
@@ -220,8 +263,8 @@ function App() {
...
@@ -220,8 +263,8 @@ function App() {
</
div
>
</
div
>
<
div
className=
"skill-groups"
>
<
div
className=
"skill-groups"
>
{
skillGroups
.
map
((
group
)
=>
(
{
skillGroups
.
map
((
group
,
index
)
=>
(
<
div
className=
"skill-group
"
key=
{
group
.
title
}
>
<
div
className=
"skill-group
reveal"
key=
{
group
.
title
}
style=
{
{
'--delay'
:
`${index * 80}ms`
}
as
CSSProperties
}
>
<
h3
>
{
group
.
title
}
</
h3
>
<
h3
>
{
group
.
title
}
</
h3
>
<
ul
>
<
ul
>
{
group
.
items
.
map
((
item
)
=>
(
{
group
.
items
.
map
((
item
)
=>
(
...
@@ -234,7 +277,7 @@ function App() {
...
@@ -234,7 +277,7 @@ function App() {
</
section
>
</
section
>
<
section
className=
"section ai-panel"
id=
"ai"
>
<
section
className=
"section ai-panel"
id=
"ai"
>
<
div
className=
"ai-copy"
>
<
div
className=
"ai-copy
reveal
"
>
<
p
className=
"section-kicker"
>
AI applied
</
p
>
<
p
className=
"section-kicker"
>
AI applied
</
p
>
<
h2
>
Profile assistant
</
h2
>
<
h2
>
Profile assistant
</
h2
>
<
p
>
<
p
>
...
@@ -243,10 +286,11 @@ function App() {
...
@@ -243,10 +286,11 @@ function App() {
</
p
>
</
p
>
</
div
>
</
div
>
<
div
className=
"assistant-box"
>
<
div
className=
"assistant-box
reveal
"
>
<
div
className=
"assistant-header"
>
<
div
className=
"assistant-header"
>
<
Bot
size=
{
22
}
/>
<
Bot
size=
{
22
}
/>
<
span
>
Ask about Hung
</
span
>
<
span
>
Ask about Hung
</
span
>
<
i
aria
-
hidden=
"true"
/>
</
div
>
</
div
>
<
div
className=
"quick-questions"
>
<
div
className=
"quick-questions"
>
...
@@ -272,14 +316,14 @@ function App() {
...
@@ -272,14 +316,14 @@ function App() {
</
button
>
</
button
>
</
div
>
</
div
>
<
div
className=
"answer-box"
>
<
div
className=
"answer-box"
key=
{
answerPulse
}
>
<
span
>
Answer
</
span
>
<
span
>
Answer
</
span
>
<
p
>
{
answer
}
</
p
>
<
p
>
{
answer
}
</
p
>
</
div
>
</
div
>
</
div
>
</
div
>
</
section
>
</
section
>
<
section
className=
"section contact-section"
id=
"contact"
>
<
section
className=
"section contact-section
reveal
"
id=
"contact"
>
<
div
>
<
div
>
<
p
className=
"section-kicker"
>
Contact
</
p
>
<
p
className=
"section-kicker"
>
Contact
</
p
>
<
h2
>
Ready for mobile app internship work.
</
h2
>
<
h2
>
Ready for mobile app internship work.
</
h2
>
...
...
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