This commit is contained in:
parent
d6960d0980
commit
2746f73cff
194
assets/index.css
194
assets/index.css
@ -5,7 +5,6 @@
|
|||||||
--text-bright: #cef0f0;
|
--text-bright: #cef0f0;
|
||||||
--glow-color: rgba(42, 90, 90, 0.6);
|
--glow-color: rgba(42, 90, 90, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
box-shadow: 0 0 5px var(--glow-color);
|
box-shadow: 0 0 5px var(--glow-color);
|
||||||
@ -20,7 +19,6 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideInBorder {
|
@keyframes slideInBorder {
|
||||||
0% {
|
0% {
|
||||||
width: 0;
|
width: 0;
|
||||||
@ -31,7 +29,6 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-snap-type: y mandatory;
|
scroll-snap-type: y mandatory;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
@ -40,7 +37,6 @@ html {
|
|||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
@ -51,7 +47,6 @@ body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::before {
|
body::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -69,14 +64,12 @@ body::before {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal {
|
.terminal {
|
||||||
margin-left: 80px;
|
margin-left: 80px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal::before {
|
.terminal::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -89,7 +82,6 @@ body::before {
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
clip-path: polygon(0 2%, 98% 0, 100% 98%, 2% 100%);
|
clip-path: polygon(0 2%, 98% 0, 100% 98%, 2% 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -106,7 +98,6 @@ body::before {
|
|||||||
box-shadow: 1px 0 15px var(--glow-color);
|
box-shadow: 1px 0 15px var(--glow-color);
|
||||||
clip-path: polygon(0 0, 90% 0, 100% 2%, 100% 98%, 90% 100%, 0 100%);
|
clip-path: polygon(0 0, 90% 0, 100% 2%, 100% 98%, 90% 100%, 0 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon {
|
.nav-icon {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -117,12 +108,10 @@ body::before {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon .icon {
|
.nav-icon .icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon .nav-label {
|
.nav-icon .nav-label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
@ -138,35 +127,28 @@ body::before {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon.active .nav-label {
|
.nav-icon.active .nav-label {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(-50%) translateY(0);
|
transform: translateX(-50%) translateY(0);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon:hover .nav-label {
|
.nav-icon:hover .nav-label {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(-50%) translateY(0);
|
transform: translateX(-50%) translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon:hover .icon {
|
.nav-icon:hover .icon {
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon.active .icon {
|
.nav-icon.active .icon {
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon.active {
|
.nav-icon.active {
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon:hover {
|
.nav-icon:hover {
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
@ -175,12 +157,10 @@ body::before {
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#contact {
|
#contact {
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
@ -190,7 +170,6 @@ body::before {
|
|||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title::before {
|
.section-title::before {
|
||||||
content: "◈";
|
content: "◈";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -200,14 +179,12 @@ body::before {
|
|||||||
text-shadow: 0 0 10px var(--glow-color);
|
text-shadow: 0 0 10px var(--glow-color);
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header::after {
|
.header::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -225,24 +202,20 @@ body::before {
|
|||||||
pulse 2s infinite,
|
pulse 2s infinite,
|
||||||
slideInBorder 1s ease-out;
|
slideInBorder 1s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timestamp {
|
.timestamp {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-family: "Courier New", monospace;
|
font-family: "Courier New", monospace;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.location {
|
.location {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-list {
|
.entry-list {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nested-entries {
|
.nested-entries {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
@ -250,7 +223,6 @@ body::before {
|
|||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nested-entries::before {
|
.nested-entries::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -261,31 +233,26 @@ body::before {
|
|||||||
background: var(--border-color);
|
background: var(--border-color);
|
||||||
box-shadow: 0 0 8px var(--glow-color);
|
box-shadow: 0 0 8px var(--glow-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry::before {
|
.entry::before {
|
||||||
content: "▹";
|
content: "▹";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-link {
|
.entry-link {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-link:hover {
|
.entry-link:hover {
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-link::after {
|
.entry-link::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -306,12 +273,10 @@ body::before {
|
|||||||
box-shadow: 0 0 8px var(--glow-color);
|
box-shadow: 0 0 8px var(--glow-color);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-link:hover::after {
|
.entry-link:hover::after {
|
||||||
transform: scaleX(1);
|
transform: scaleX(1);
|
||||||
transform-origin: left;
|
transform-origin: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-item,
|
.project-item,
|
||||||
.status-item {
|
.status-item {
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
@ -349,7 +314,6 @@ body::before {
|
|||||||
border-left: 3px solid rgba(42, 90, 90, 0.6);
|
border-left: 3px solid rgba(42, 90, 90, 0.6);
|
||||||
border-top: 2px solid rgba(255, 255, 255, 0.15);
|
border-top: 2px solid rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-item:hover,
|
.project-item:hover,
|
||||||
.status-item:hover {
|
.status-item:hover {
|
||||||
border-color: var(--text-bright);
|
border-color: var(--text-bright);
|
||||||
@ -361,35 +325,29 @@ body::before {
|
|||||||
transform: translateY(-2px) translateZ(10px);
|
transform: translateY(-2px) translateZ(10px);
|
||||||
animation: pulse 2s infinite;
|
animation: pulse 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-header {
|
.project-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-header .entry::before {
|
.project-header .entry::before {
|
||||||
content: "◈";
|
content: "◈";
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
text-shadow: 0 0 5px var(--glow-color);
|
text-shadow: 0 0 5px var(--glow-color);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-item:hover .project-header .entry::before {
|
.project-item:hover .project-header .entry::before {
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
text-shadow: 0 0 10px var(--glow-color);
|
text-shadow: 0 0 10px var(--glow-color);
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-icon {
|
.expand-icon {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-item.expanded .expand-icon {
|
.project-item.expanded .expand-icon {
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-content {
|
.project-content {
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -401,13 +359,11 @@ body::before {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-10px);
|
transform: translateY(-10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-content.expanded {
|
.project-content.expanded {
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-link {
|
.project-link {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--text-bright);
|
color: var(--text-bright);
|
||||||
@ -422,17 +378,14 @@ body::before {
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-link:hover {
|
.project-link:hover {
|
||||||
border-color: var(--text-bright);
|
border-color: var(--text-bright);
|
||||||
background: rgba(42, 90, 90, 0.1);
|
background: rgba(42, 90, 90, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-grid {
|
.status-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-indicator {
|
.status-indicator {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@ -440,22 +393,18 @@ body::before {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-online {
|
.status-online {
|
||||||
background-color: #4caf50;
|
background-color: #4caf50;
|
||||||
box-shadow: 0 0 10px rgba(76, 175, 80, 0.3);
|
box-shadow: 0 0 10px rgba(76, 175, 80, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-offline {
|
.status-offline {
|
||||||
background-color: #f44336;
|
background-color: #f44336;
|
||||||
box-shadow: 0 0 10px rgba(244, 67, 54, 0.3);
|
box-shadow: 0 0 10px rgba(244, 67, 54, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-warning {
|
.status-warning {
|
||||||
background-color: #ff9800;
|
background-color: #ff9800;
|
||||||
box-shadow: 0 0 10px rgba(255, 152, 0, 0.3);
|
box-shadow: 0 0 10px rgba(255, 152, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-details {
|
.status-details {
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -467,20 +416,17 @@ body::before {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-10px);
|
transform: translateY(-10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-details.expanded {
|
.status-details.expanded {
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-links {
|
.contact-links {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-link {
|
.contact-link {
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
@ -517,7 +463,6 @@ body::before {
|
|||||||
border-left: 3px solid rgba(42, 90, 90, 0.6);
|
border-left: 3px solid rgba(42, 90, 90, 0.6);
|
||||||
border-top: 2px solid rgba(255, 255, 255, 0.15);
|
border-top: 2px solid rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-link:hover {
|
.contact-link:hover {
|
||||||
border-color: var(--text-bright);
|
border-color: var(--text-bright);
|
||||||
background: rgba(42, 90, 90, 0.1);
|
background: rgba(42, 90, 90, 0.1);
|
||||||
@ -528,53 +473,43 @@ body::before {
|
|||||||
transform: translateY(-2px) translateZ(10px);
|
transform: translateY(-2px) translateZ(10px);
|
||||||
animation: pulse 2s infinite;
|
animation: pulse 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-icon {
|
.contact-icon {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.terminal {
|
.terminal {
|
||||||
margin-left: 60px;
|
margin-left: 60px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon {
|
.nav-icon {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-item,
|
.project-item,
|
||||||
.contact-link,
|
.contact-link,
|
||||||
.status-item {
|
.status-item {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.terminal {
|
.terminal {
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-icon {
|
.nav-icon {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-average {
|
.status-average {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
@ -582,7 +517,6 @@ body::before {
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-metrics {
|
.header-metrics {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
@ -592,7 +526,133 @@ body::before {
|
|||||||
font-family: "Courier New", monospace;
|
font-family: "Courier New", monospace;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
color: var(--border-color);
|
color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
.contact-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.contact-card {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(42, 90, 90, 0.05) 0%,
|
||||||
|
rgba(42, 90, 90, 0.1) 100%
|
||||||
|
);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
clip-path: polygon(
|
||||||
|
0 0,
|
||||||
|
95% 0,
|
||||||
|
100% 20px,
|
||||||
|
100% 100%,
|
||||||
|
5% 100%,
|
||||||
|
0 calc(100% - 20px)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.contact-header {
|
||||||
|
padding: 15px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.contact-type {
|
||||||
|
color: var(--text-bright);
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
.connection-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
.status-dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background: var(--text-bright);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 8px;
|
||||||
|
box-shadow: 0 0 8px var(--glow-color);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
.contact-content {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
.contact-method {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
margin: 8px 0;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-muted);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: rgba(42, 90, 90, 0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.contact-method:hover {
|
||||||
|
color: var(--text-bright);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
background: rgba(42, 90, 90, 0.2);
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
.contact-method::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 2px;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--text-bright);
|
||||||
|
box-shadow: 0 0 10px var(--glow-color);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
.contact-method:hover::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.method-icon {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-right: 15px;
|
||||||
|
color: var(--text-bright);
|
||||||
|
}
|
||||||
|
.method-details {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.method-name {
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.method-address {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
.connection-indicator {
|
||||||
|
margin-left: 10px;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
.contact-method:hover .connection-indicator {
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.contact-grid {
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
.contact-method {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.contact-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.method-details {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
144
assets/index.js
144
assets/index.js
@ -1,8 +1,21 @@
|
|||||||
// this is slop. i know. i'm sorry.
|
// animation speeds (ms)
|
||||||
// atleast i'm not a reactfag.
|
const TYPING_SPEED = 180;
|
||||||
|
const ERASE_SPEED = 100;
|
||||||
|
const STATUS_UPDATE_INTERVAL = 60000; // 1min
|
||||||
|
const TIME_UPDATE_INTERVAL = 1; // 1ms (still terrible)
|
||||||
|
|
||||||
// we add some fancy text typing animation blah blah blah
|
// status thresholds
|
||||||
// it is quite overcomplicated. i have no hobbies
|
const LATENCY_WARNING_THRESHOLD = 500000;
|
||||||
|
|
||||||
|
// mapping for group names because i'm lazy
|
||||||
|
const GROUP_NAMES = {
|
||||||
|
4: "INFRASTRUCTURE",
|
||||||
|
5: "SERVICES",
|
||||||
|
6: "API",
|
||||||
|
};
|
||||||
|
|
||||||
|
// fancy text typing animation for nav labels
|
||||||
|
// overcomplicated as fuck but whatever
|
||||||
function writeoutnavlabel(label) {
|
function writeoutnavlabel(label) {
|
||||||
if (label.getAttribute("data-animating") === "true") return;
|
if (label.getAttribute("data-animating") === "true") return;
|
||||||
|
|
||||||
@ -26,18 +39,17 @@ function writeoutnavlabel(label) {
|
|||||||
icon.hoverAnimationDone = true;
|
icon.hoverAnimationDone = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 180);
|
}, TYPING_SPEED);
|
||||||
|
|
||||||
label.setAttribute("data-animation-interval", animationInterval);
|
label.setAttribute("data-animation-interval", animationInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// erase animation for nav labels
|
||||||
|
// also terrible but consistent with the above
|
||||||
function eraselabel(label) {
|
function eraselabel(label) {
|
||||||
if (label.getAttribute("data-animating") === "true") {
|
if (label.getAttribute("data-animating") === "true") {
|
||||||
// clear the existing interval
|
|
||||||
const existingInterval = label.getAttribute("data-animation-interval");
|
const existingInterval = label.getAttribute("data-animation-interval");
|
||||||
if (existingInterval) {
|
if (existingInterval) clearInterval(existingInterval);
|
||||||
clearInterval(existingInterval);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
label.setAttribute("data-animating", "true");
|
label.setAttribute("data-animating", "true");
|
||||||
@ -51,13 +63,13 @@ function eraselabel(label) {
|
|||||||
label.setAttribute("data-animating", "false");
|
label.setAttribute("data-animating", "false");
|
||||||
label.style.opacity = "0";
|
label.style.opacity = "0";
|
||||||
}
|
}
|
||||||
}, 100);
|
}, ERASE_SPEED);
|
||||||
|
|
||||||
// store the interval so we can clear it if needed
|
|
||||||
// this is a terrible way to do this like horrible
|
|
||||||
label.setAttribute("data-animation-interval", eraseInterval);
|
label.setAttribute("data-animation-interval", eraseInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// time display updater
|
||||||
|
// still updates way too frequently but whatever
|
||||||
function updateTime() {
|
function updateTime() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const timeString = now
|
const timeString = now
|
||||||
@ -75,13 +87,9 @@ function updateTime() {
|
|||||||
document.getElementById("current-time").textContent = timeString;
|
document.getElementById("current-time").textContent = timeString;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTime();
|
// status helper functions
|
||||||
// this is a terrible way to update the time
|
|
||||||
setInterval(updateTime, 1);
|
|
||||||
|
|
||||||
function calculateAverageUptime(services) {
|
function calculateAverageUptime(services) {
|
||||||
if (!services || services.length === 0) return 0;
|
if (!services || services.length === 0) return 0;
|
||||||
// did i ever mention that i hate math?
|
|
||||||
const totalUptime = services.reduce(
|
const totalUptime = services.reduce(
|
||||||
(sum, service) => sum + service.online_24_hours,
|
(sum, service) => sum + service.online_24_hours,
|
||||||
0,
|
0,
|
||||||
@ -89,28 +97,6 @@ function calculateAverageUptime(services) {
|
|||||||
return (totalUptime / services.length).toFixed(2);
|
return (totalUptime / services.length).toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// projects
|
|
||||||
const projectItems = document.querySelectorAll(".project-item");
|
|
||||||
projectItems.forEach((item) => {
|
|
||||||
item.addEventListener("click", function () {
|
|
||||||
const content = this.querySelector(".project-content");
|
|
||||||
const icon = this.querySelector(".expand-icon");
|
|
||||||
|
|
||||||
// close other expanded items
|
|
||||||
projectItems.forEach((otherItem) => {
|
|
||||||
if (otherItem !== item) {
|
|
||||||
otherItem
|
|
||||||
.querySelector(".project-content")
|
|
||||||
.classList.remove("expanded");
|
|
||||||
otherItem.querySelector(".expand-icon").textContent = "+";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
content.classList.toggle("expanded");
|
|
||||||
icon.textContent = content.classList.contains("expanded") ? "−" : "+";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// status updater
|
|
||||||
function formatLatency(latency) {
|
function formatLatency(latency) {
|
||||||
return (Math.round((latency / 1000) * 10) / 10).toFixed(1);
|
return (Math.round((latency / 1000) * 10) / 10).toFixed(1);
|
||||||
}
|
}
|
||||||
@ -121,20 +107,16 @@ function formatUptime(uptime) {
|
|||||||
|
|
||||||
function getStatusClass(service) {
|
function getStatusClass(service) {
|
||||||
if (!service.online) return "status-offline";
|
if (!service.online) return "status-offline";
|
||||||
if (service.latency > 500000) return "status-warning";
|
if (service.latency > LATENCY_WARNING_THRESHOLD) return "status-warning";
|
||||||
return "status-online";
|
return "status-online";
|
||||||
}
|
}
|
||||||
// not sure if this is the best way to do this
|
|
||||||
function getGroupName(groupId) {
|
function getGroupName(groupId) {
|
||||||
const groupNames = {
|
return GROUP_NAMES[groupId] || `GROUP ${groupId}`;
|
||||||
4: "INFRASTRUCTURE",
|
|
||||||
5: "SERVICES",
|
|
||||||
6: "API",
|
|
||||||
};
|
|
||||||
return groupNames[groupId] || `GROUP ${groupId}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate status page first
|
// creates the HTML for the status page
|
||||||
|
// groups services by their group_id
|
||||||
function createStatusHTML(services) {
|
function createStatusHTML(services) {
|
||||||
const groups = services.reduce((acc, service) => {
|
const groups = services.reduce((acc, service) => {
|
||||||
const groupId = service.group_id;
|
const groupId = service.group_id;
|
||||||
@ -172,7 +154,7 @@ function createStatusHTML(services) {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// status page func
|
// main status update function
|
||||||
function updateStatus() {
|
function updateStatus() {
|
||||||
const statusContainer = document.getElementById("status-container");
|
const statusContainer = document.getElementById("status-container");
|
||||||
const averageUptimeElement = document.getElementById("average-uptime");
|
const averageUptimeElement = document.getElementById("average-uptime");
|
||||||
@ -194,7 +176,9 @@ function updateStatus() {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
statusContainer.innerHTML = `
|
statusContainer.innerHTML = `
|
||||||
<div class="error-message">
|
<div class="error-message">
|
||||||
UNABLE TO FETCH STATUS DATA
|
a(42, 90, 90, 0.3), 0 0 40px rgba(42, 90, 90, 0.2); } to { text-shadow: 0 0 40px rgba(42, 90, 90, 0.5), 0 0 80px rgba(42, 90, 90, 0.3); } }"
|
||||||
|
);
|
||||||
|
}); UNABLE TO FETCH STATUS DATA
|
||||||
<div style="font-size: 0.8em; margin-top: 5px;">
|
<div style="font-size: 0.8em; margin-top: 5px;">
|
||||||
${error.message}
|
${error.message}
|
||||||
</div>
|
</div>
|
||||||
@ -204,20 +188,28 @@ function updateStatus() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial update
|
// project expansion handlers
|
||||||
updateStatus();
|
document.querySelectorAll(".project-item").forEach((item) => {
|
||||||
|
|
||||||
// status details toggle
|
|
||||||
document.querySelectorAll(".status-item .entry").forEach((item) => {
|
|
||||||
const details = item.nextElementSibling;
|
|
||||||
if (details && details.classList.contains("status-details")) {
|
|
||||||
item.addEventListener("click", function () {
|
item.addEventListener("click", function () {
|
||||||
details.classList.toggle("expanded");
|
const content = this.querySelector(".project-content");
|
||||||
});
|
const icon = this.querySelector(".expand-icon");
|
||||||
|
|
||||||
|
// close other expanded items
|
||||||
|
document.querySelectorAll(".project-item").forEach((otherItem) => {
|
||||||
|
if (otherItem !== item) {
|
||||||
|
otherItem
|
||||||
|
.querySelector(".project-content")
|
||||||
|
.classList.remove("expanded");
|
||||||
|
otherItem.querySelector(".expand-icon").textContent = "+";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// navigation
|
content.classList.toggle("expanded");
|
||||||
|
icon.textContent = content.classList.contains("expanded") ? "−" : "+";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// smooth scroll navigation
|
||||||
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
||||||
anchor.addEventListener("click", function (e) {
|
anchor.addEventListener("click", function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -227,7 +219,7 @@ document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// update active nav icon
|
// active nav icon updater
|
||||||
window.addEventListener("scroll", () => {
|
window.addEventListener("scroll", () => {
|
||||||
let current = "";
|
let current = "";
|
||||||
document.querySelectorAll("section").forEach((section) => {
|
document.querySelectorAll("section").forEach((section) => {
|
||||||
@ -245,7 +237,6 @@ window.addEventListener("scroll", () => {
|
|||||||
if (isCurrentSection) {
|
if (isCurrentSection) {
|
||||||
if (!wasActive) {
|
if (!wasActive) {
|
||||||
icon.classList.add("active");
|
icon.classList.add("active");
|
||||||
// complete instantly
|
|
||||||
if (icon.isAnimating) {
|
if (icon.isAnimating) {
|
||||||
const text = label.getAttribute("data-text") || label.textContent;
|
const text = label.getAttribute("data-text") || label.textContent;
|
||||||
label.textContent = text;
|
label.textContent = text;
|
||||||
@ -254,7 +245,6 @@ window.addEventListener("scroll", () => {
|
|||||||
parseInt(label.getAttribute("data-animation-interval")),
|
parseInt(label.getAttribute("data-animation-interval")),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// only start if not currently animating
|
|
||||||
if (!icon.hoverAnimationDone) {
|
if (!icon.hoverAnimationDone) {
|
||||||
writeoutnavlabel(label);
|
writeoutnavlabel(label);
|
||||||
}
|
}
|
||||||
@ -271,6 +261,7 @@ window.addEventListener("scroll", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// nav icon hover animations
|
||||||
document.querySelectorAll(".nav-icon").forEach((icon) => {
|
document.querySelectorAll(".nav-icon").forEach((icon) => {
|
||||||
icon.hoverAnimationDone = false;
|
icon.hoverAnimationDone = false;
|
||||||
icon.isAnimating = false;
|
icon.isAnimating = false;
|
||||||
@ -292,36 +283,39 @@ document.querySelectorAll(".nav-icon").forEach((icon) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
icon.addEventListener("click", (e) => {
|
icon.addEventListener("click", () => {
|
||||||
const label = icon.querySelector(".nav-label");
|
const label = icon.querySelector(".nav-label");
|
||||||
|
|
||||||
// continue if already active
|
|
||||||
// prevent mouseleave from erasing label
|
|
||||||
if (icon.isAnimating) {
|
if (icon.isAnimating) {
|
||||||
icon.hoverAnimationDone = true;
|
icon.hoverAnimationDone = true;
|
||||||
}
|
} else if (!icon.hoverAnimationDone) {
|
||||||
// if no animation, start one
|
|
||||||
else if (!icon.hoverAnimationDone) {
|
|
||||||
writeoutnavlabel(label);
|
writeoutnavlabel(label);
|
||||||
icon.isAnimating = true;
|
icon.isAnimating = true;
|
||||||
icon.hoverAnimationDone = true;
|
icon.hoverAnimationDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
label.style.opacity = "1";
|
label.style.opacity = "1";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// dom (196 reference) content event listener
|
// initialization
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
// write out active nav icon label instantly
|
// start time updates
|
||||||
|
updateTime();
|
||||||
|
setInterval(updateTime, TIME_UPDATE_INTERVAL);
|
||||||
|
|
||||||
|
// start status updates
|
||||||
|
updateStatus();
|
||||||
|
setInterval(updateStatus, STATUS_UPDATE_INTERVAL);
|
||||||
|
|
||||||
|
// init active nav icon
|
||||||
const activeNavIcon = document.querySelector(".nav-icon.active");
|
const activeNavIcon = document.querySelector(".nav-icon.active");
|
||||||
if (activeNavIcon) {
|
if (activeNavIcon) {
|
||||||
const label = activeNavIcon.querySelector(".nav-label");
|
const label = activeNavIcon.querySelector(".nav-label");
|
||||||
if (label) writeoutnavlabel(label);
|
if (label) writeoutnavlabel(label);
|
||||||
}
|
}
|
||||||
// fancy console log
|
|
||||||
|
// fancy console log because why not
|
||||||
console.log(
|
console.log(
|
||||||
"%cELIA.NETWORK",
|
"%cELIA.NETWORK",
|
||||||
"color: #2a5a5a; font-size: 24px; font-weight: bold; text-shadow: 2px 2px 4px rgba(42, 90, 90, 0.4), 4px 4px 8px rgba(42, 90, 90, 0.2), 6px 6px 12px rgba(42, 90, 90, 0.1), 0 0 20px rgba(42, 90, 90, 0.3); transform: perspective(1000px) rotateX(20deg) rotateY(-10deg) translateZ(50px); animation: console3d 8s infinite cubic-bezier(0.4, 0, 0.2, 1), glow 2s infinite alternate; @keyframes console3d { 0% { transform: perspective(1000px) rotateX(20deg) rotateY(-10deg) translateZ(50px); } 25% { transform: perspective(1000px) rotateX(-15deg) rotateY(15deg) translateZ(75px); } 50% { transform: perspective(1000px) rotateX(10deg) rotateY(20deg) translateZ(100px); } 75% { transform: perspective(1000px) rotateX(-20deg) rotateY(-15deg) translateZ(75px); } 100% { transform: perspective(1000px) rotateX(20deg) rotateY(-10deg) translateZ(50px); } } @keyframes glow { from { text-shadow: 0 0 20px rgba(42, 90, 90, 0.3), 0 0 40px rgba(42, 90, 90, 0.2); } to { text-shadow: 0 0 40px rgba(42, 90, 90, 0.5), 0 0 80px rgba(42, 90, 90, 0.3); } }",
|
"color: #2a5a5a; font-size: 24px; font-weight: bold; text-shadow: 2px 2px 4px rgba(42, 90, 90, 0.3);",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
67
index.html
67
index.html
@ -202,41 +202,52 @@
|
|||||||
|
|
||||||
<section id="contact" class="section">
|
<section id="contact" class="section">
|
||||||
<div class="section-title">CONTACT</div>
|
<div class="section-title">CONTACT</div>
|
||||||
<div class="contact-links">
|
<div class="contact-grid">
|
||||||
<div class="project-item">
|
<div class="contact-card">
|
||||||
<div class="project-header">
|
<div class="contact-header">
|
||||||
<div class="entry">E-Mail</div>
|
<div class="contact-type">NETWORK</div>
|
||||||
<span class="expand-icon">+</span>
|
<div class="connection-status">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
EMAIL
|
||||||
</div>
|
</div>
|
||||||
<div class="project-content">
|
|
||||||
<div class="project-links">
|
|
||||||
<a
|
|
||||||
href="mailto:alerts@elia.network"
|
|
||||||
class="project-link"
|
|
||||||
>⎔ ALERTS →</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href="mailto:noc@elia.network"
|
|
||||||
class="project-link"
|
|
||||||
>⎔ NOC →</a
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="contact-content">
|
||||||
|
<a href="mailto:alerts@elia.network" class="contact-method">
|
||||||
|
<span class="method-icon">⌘</span>
|
||||||
|
<span class="method-details">
|
||||||
|
<span class="method-name">ALERTS</span>
|
||||||
|
<span class="method-address">alerts@elia.network</span>
|
||||||
|
</span>
|
||||||
|
<span class="connection-indicator">→</span>
|
||||||
|
</a>
|
||||||
|
<a href="mailto:noc@elia.network" class="contact-method">
|
||||||
|
<span class="method-icon">⌘</span>
|
||||||
|
<span class="method-details">
|
||||||
|
<span class="method-name">NOC</span>
|
||||||
|
<span class="method-address">noc@elia.network</span>
|
||||||
|
</span>
|
||||||
|
<span class="connection-indicator">→</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="project-item">
|
<div class="contact-card">
|
||||||
<div class="project-header">
|
<div class="contact-header">
|
||||||
<div class="entry">TELEGRAM</div>
|
<div class="contact-type">DIRECT</div>
|
||||||
<span class="expand-icon">+</span>
|
<div class="connection-status">
|
||||||
|
<span class="status-dot"></span>
|
||||||
|
INSTANT MESSAGING
|
||||||
</div>
|
</div>
|
||||||
<div class="project-content">
|
|
||||||
<div class="project-links">
|
|
||||||
<a
|
|
||||||
href="https://t.me/beslikmeister"
|
|
||||||
class="project-link"
|
|
||||||
>⎔ T.ME →</a
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="contact-content">
|
||||||
|
<a href="https://t.me/beslikmeister" class="contact-method">
|
||||||
|
<span class="method-icon">⌥</span>
|
||||||
|
<span class="method-details">
|
||||||
|
<span class="method-name">TELEGRAM</span>
|
||||||
|
<span class="method-address">@beslikmeister</span>
|
||||||
|
</span>
|
||||||
|
<span class="connection-indicator">→</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user