Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb18a6f289 | ||
|
|
6f702eafcb | ||
|
|
556f5761fd |
+20
-50
@@ -3,81 +3,51 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
margin: 0 0 0.4rem;
|
margin: 0 0 0.4rem;
|
||||||
text-shadow: 0 0 20px var(--color-accent-dim);
|
text-shadow: 0 0 24px oklch(55% 0.19 142 / 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-subheading {
|
.page-subheading {
|
||||||
color: var(--color-silver);
|
color: var(--color-silver);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
margin: 0 0 2rem;
|
margin: 0 0 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-grid {
|
.project-grid {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
flex-direction: column;
|
||||||
gap: 1.5rem;
|
gap: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card {
|
.project-card {
|
||||||
border: var(--border);
|
border: var(--border);
|
||||||
background: var(--color-bg-raised);
|
padding: 1.5rem;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card__bar {
|
.project-card__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: baseline;
|
||||||
gap: 0.5rem;
|
justify-content: space-between;
|
||||||
padding: 0.5rem 0.75rem;
|
gap: 0.75rem;
|
||||||
border-bottom: var(--border);
|
flex-wrap: wrap;
|
||||||
color: var(--color-silver);
|
margin-bottom: 0.6rem;
|
||||||
font-size: 0.8rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card__bar span {
|
.project-card__name {
|
||||||
width: 9px;
|
font-size: 1.05rem;
|
||||||
height: 9px;
|
font-weight: 600;
|
||||||
border-radius: 50%;
|
color: var(--color-fg-dim);
|
||||||
background: var(--color-accent-dim);
|
|
||||||
opacity: 0.6;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card__perms {
|
.project-card__perms {
|
||||||
color: var(--color-accent-dim);
|
color: var(--color-accent-mid);
|
||||||
}
|
|
||||||
|
|
||||||
.project-card__body {
|
|
||||||
padding: 1.25rem 1.5rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.75rem;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card__title {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--color-accent);
|
|
||||||
font-size: 1.05rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card__desc {
|
.project-card__desc {
|
||||||
margin: 0;
|
margin: 0 0 0.75rem;
|
||||||
color: var(--color-fg);
|
color: var(--color-fg-dim);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
line-height: 1.6;
|
||||||
|
|
||||||
.project-card__link {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card__link::before {
|
|
||||||
content: '$ ';
|
|
||||||
color: var(--color-accent-dim);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-30
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
.hero-text .tagline {
|
.hero-text .tagline {
|
||||||
margin: 0.75rem 0 0;
|
margin: 0.75rem 0 0;
|
||||||
color: var(--color-accent-dim);
|
color: var(--color-accent-mid);
|
||||||
max-width: 34ch;
|
max-width: 34ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
letter-spacing: 0.12em;
|
letter-spacing: 0.12em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: var(--color-accent-dim);
|
color: var(--color-accent-mid);
|
||||||
margin: 0 0 0.75rem;
|
margin: 0 0 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
height: 7px;
|
height: 7px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--color-accent);
|
background: var(--color-accent);
|
||||||
box-shadow: 0 0 8px var(--color-accent-dim);
|
box-shadow: 0 0 10px var(--color-accent);
|
||||||
animation: blink 1.6s step-end infinite;
|
animation: blink 1.6s step-end infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,49 +48,62 @@ section {
|
|||||||
|
|
||||||
.about p {
|
.about p {
|
||||||
max-width: 68ch;
|
max-width: 68ch;
|
||||||
|
font-size: 0.94rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--color-fg);
|
||||||
|
margin: 0 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline {
|
.timeline {
|
||||||
border-left: var(--border);
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 0 0 1.5rem;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-item {
|
.timeline-item {
|
||||||
position: relative;
|
border-left: var(--border);
|
||||||
margin-bottom: 2rem;
|
padding-left: 1rem;
|
||||||
}
|
margin-bottom: 1.75rem;
|
||||||
|
|
||||||
.timeline-item::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: -1.6rem;
|
|
||||||
top: 0.4rem;
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
background: var(--color-accent);
|
|
||||||
box-shadow: 0 0 6px var(--color-accent-dim);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-item__meta {
|
.timeline-item__meta {
|
||||||
color: var(--color-silver);
|
color: var(--color-silver-dim);
|
||||||
font-size: 0.85rem;
|
font-size: 0.78rem;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-item__role {
|
.timeline-item__role {
|
||||||
color: var(--color-accent);
|
color: var(--color-fg);
|
||||||
margin: 0.15rem 0 0.5rem;
|
font-weight: 600;
|
||||||
font-size: 1rem;
|
margin: 0 0 0.15rem;
|
||||||
|
font-size: 0.94rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item__company {
|
||||||
|
color: var(--color-silver);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-item ul {
|
.timeline-item ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 1.2rem;
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-item li::marker {
|
.timeline-item li {
|
||||||
color: var(--color-accent-dim);
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--color-fg-dim);
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item .dash {
|
||||||
|
color: var(--color-accent-mid);
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-list {
|
.contact-list {
|
||||||
@@ -99,12 +112,13 @@ section {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.4rem;
|
gap: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-list a::before {
|
.contact-list__label {
|
||||||
content: '$ ';
|
color: var(--color-silver-dim);
|
||||||
color: var(--color-accent-dim);
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skill-group {
|
.skill-group {
|
||||||
|
|||||||
+36
-46
@@ -1,12 +1,17 @@
|
|||||||
:root {
|
:root {
|
||||||
--color-bg: #0a0e0c;
|
--color-bg: #050705;
|
||||||
--color-bg-raised: #0f1512;
|
--color-bg-raised: #0d100d;
|
||||||
--color-fg: #c7c7c7;
|
--color-fg: oklch(84% 0.01 240);
|
||||||
--color-accent: #00ff41;
|
--color-fg-dim: oklch(76% 0.01 240);
|
||||||
--color-accent-dim: #0a8f2c;
|
--color-accent: oklch(80% 0.19 142);
|
||||||
--color-silver: #9fa3a0;
|
--color-accent-bright: oklch(88% 0.17 142);
|
||||||
--font-mono: 'JetBrains Mono', 'Share Tech Mono', ui-monospace, monospace;
|
--color-accent-mid: oklch(55% 0.13 142);
|
||||||
--border: 1px solid #163020;
|
--color-accent-dim: oklch(50% 0.13 142);
|
||||||
|
--color-silver: oklch(58% 0.01 240);
|
||||||
|
--color-silver-dim: oklch(48% 0.01 240);
|
||||||
|
--color-num: oklch(40% 0.02 240);
|
||||||
|
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
||||||
|
--border: 1px solid oklch(32% 0.06 142 / 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -25,9 +30,19 @@ body {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* лёгкий градиент глубины поверх дождя, под контентом */
|
||||||
|
body::after {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(180deg, rgba(4, 6, 4, 0.35), rgba(4, 6, 4, 0.55) 220px, rgba(4, 6, 4, 0.6));
|
||||||
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background: var(--color-accent-dim);
|
background: oklch(42% 0.14 142);
|
||||||
color: var(--color-bg);
|
color: #eafff0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -67,11 +82,10 @@ body::before {
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: repeating-linear-gradient(
|
background: repeating-linear-gradient(
|
||||||
to bottom,
|
180deg,
|
||||||
rgba(0, 0, 0, 0.15) 0px,
|
rgba(0, 0, 0, 0) 0px,
|
||||||
rgba(0, 0, 0, 0.15) 1px,
|
rgba(0, 0, 0, 0) 2px,
|
||||||
transparent 1px,
|
rgba(80, 255, 140, 0.025) 3px
|
||||||
transparent 3px
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +95,7 @@ body::before {
|
|||||||
top: 0;
|
top: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
border-bottom: var(--border);
|
border-bottom: var(--border);
|
||||||
background: rgba(10, 14, 12, 0.6);
|
background: rgba(4, 6, 4, 0.55);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
-webkit-backdrop-filter: blur(8px);
|
-webkit-backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
@@ -162,32 +176,6 @@ body::before {
|
|||||||
color: var(--color-accent-dim);
|
color: var(--color-accent-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- терминальное окно-обёртка для блоков контента --- */
|
|
||||||
.term-window {
|
|
||||||
border: var(--border);
|
|
||||||
background: var(--color-bg-raised);
|
|
||||||
margin: 1.75rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.term-window__bar {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.4rem;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
border-bottom: var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.term-window__bar span {
|
|
||||||
width: 9px;
|
|
||||||
height: 9px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--color-accent-dim);
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.term-window__body {
|
|
||||||
padding: 1.25rem 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- ASCII-арт --- */
|
/* --- ASCII-арт --- */
|
||||||
.ascii-cactus {
|
.ascii-cactus {
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
@@ -252,15 +240,17 @@ body::before {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
font-size: 1rem;
|
font-size: 0.9rem;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
text-transform: uppercase;
|
||||||
border-bottom: var(--border);
|
border-bottom: var(--border);
|
||||||
padding-bottom: 0.6rem;
|
padding-bottom: 0.6rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
color: var(--color-fg);
|
color: var(--color-accent-mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading__num {
|
.section-heading__num {
|
||||||
color: var(--color-accent-dim);
|
color: var(--color-num);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>./battlecity — cacto</title>
|
<title>./battlecity — cacto</title>
|
||||||
<link rel="icon" type="image/svg+xml" href="/assets/cactus-logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/assets/cactus-logo.svg" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="/css/theme.css" />
|
<link rel="stylesheet" href="/css/theme.css" />
|
||||||
<link rel="stylesheet" href="/css/game.css" />
|
<link rel="stylesheet" href="/css/game.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
<title>cacto — Гаммель Дмитрий, PHP/Go backend</title>
|
<title>cacto — Гаммель Дмитрий, PHP/Go backend</title>
|
||||||
<meta name="description" content="Гаммель Дмитрий — backend-разработчик PHP/Go. Резюме, проекты, эксперименты." />
|
<meta name="description" content="Гаммель Дмитрий — backend-разработчик PHP/Go. Резюме, проекты, эксперименты." />
|
||||||
<link rel="icon" type="image/svg+xml" href="/assets/cactus-logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/assets/cactus-logo.svg" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="/css/theme.css" />
|
<link rel="stylesheet" href="/css/theme.css" />
|
||||||
<link rel="stylesheet" href="/css/resume.css" />
|
<link rel="stylesheet" href="/css/resume.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+24
-24
@@ -2,47 +2,47 @@ const canvas = document.getElementById('matrix-rain');
|
|||||||
|
|
||||||
if (canvas && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
if (canvas && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
const chars = 'アカサタナハマヤラワ0123456789ABCDEFcacto$#@%&*'.split('');
|
const chars = 'アカサタナハマヤラワ01アイウエオカキク';
|
||||||
const fontSize = 15;
|
const fontSize = 16;
|
||||||
const bg = getComputedStyle(document.documentElement).getPropertyValue('--color-bg').trim();
|
|
||||||
|
|
||||||
let columns = 0;
|
let columns = 0;
|
||||||
let drops = [];
|
let drops = [];
|
||||||
|
let frameCount = 0;
|
||||||
|
|
||||||
function setup() {
|
function resize() {
|
||||||
canvas.width = window.innerWidth;
|
canvas.width = window.innerWidth;
|
||||||
canvas.height = window.innerHeight;
|
canvas.height = window.innerHeight;
|
||||||
columns = Math.floor(canvas.width / fontSize);
|
columns = Math.floor(canvas.width / fontSize);
|
||||||
drops = new Array(columns).fill(0).map(() => Math.floor((Math.random() * canvas.height) / fontSize));
|
drops = new Array(columns).fill(0).map(() => Math.random() * -50);
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
ctx.fillStyle = `${bg}cc`;
|
ctx.fillStyle = 'rgba(3, 5, 3, 0.12)';
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
ctx.font = `${fontSize}px monospace`;
|
ctx.font = `${fontSize}px monospace`;
|
||||||
|
|
||||||
for (let i = 0; i < columns; i++) {
|
for (let i = 0; i < drops.length; i++) {
|
||||||
const char = chars[Math.floor(Math.random() * chars.length)];
|
const text = chars[Math.floor(Math.random() * chars.length)];
|
||||||
const x = i * fontSize;
|
const isHead = Math.random() > 0.92;
|
||||||
const y = drops[i] * fontSize;
|
ctx.shadowColor = 'rgba(100, 255, 160, 1)';
|
||||||
|
ctx.shadowBlur = isHead ? 12 : 2;
|
||||||
// «голова» капли — редкая, чуть ярче, со свечением
|
ctx.fillStyle = isHead ? 'rgba(210, 255, 225, 1)' : 'rgba(80, 255, 140, 1)';
|
||||||
const isHead = Math.random() > 0.94;
|
ctx.fillText(text, i * fontSize, drops[i] * fontSize);
|
||||||
ctx.shadowColor = 'rgba(100, 255, 160, 0.9)';
|
if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
|
||||||
ctx.shadowBlur = isHead ? 10 : 0;
|
|
||||||
ctx.fillStyle = isHead ? '#d2ffe1' : Math.random() > 0.98 ? '#9fa3a0' : '#00ff41';
|
|
||||||
ctx.fillText(char, x, y);
|
|
||||||
|
|
||||||
if (y > canvas.height && Math.random() > 0.975) {
|
|
||||||
drops[i] = 0;
|
drops[i] = 0;
|
||||||
} else {
|
|
||||||
drops[i]++;
|
|
||||||
}
|
}
|
||||||
|
drops[i] += 0.4;
|
||||||
}
|
}
|
||||||
ctx.shadowBlur = 0;
|
ctx.shadowBlur = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup();
|
function tick() {
|
||||||
window.addEventListener('resize', setup);
|
frameCount++;
|
||||||
setInterval(draw, 50);
|
if (frameCount % 2 === 0) draw();
|
||||||
|
requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
resize();
|
||||||
|
window.addEventListener('resize', resize);
|
||||||
|
requestAnimationFrame(tick);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-12
@@ -7,7 +7,6 @@ const DATA = {
|
|||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'home_automatization',
|
name: 'home_automatization',
|
||||||
title: 'Home Automation Platform',
|
|
||||||
description:
|
description:
|
||||||
'Микросервисная платформа умного дома на Go и Laravel. Первая зона — гроубокс (полив, свет, климат), дальше — весь дом.',
|
'Микросервисная платформа умного дома на Go и Laravel. Первая зона — гроубокс (полив, свет, климат), дальше — весь дом.',
|
||||||
tags: ['Go', 'Laravel', 'Docker', 'RabbitMQ', 'ClickHouse', 'gRPC', 'MQTT'],
|
tags: ['Go', 'Laravel', 'Docker', 'RabbitMQ', 'ClickHouse', 'gRPC', 'MQTT'],
|
||||||
@@ -16,7 +15,6 @@ const DATA = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cactoz.su',
|
name: 'cactoz.su',
|
||||||
title: 'cactoz.su — этот сайт',
|
|
||||||
description:
|
description:
|
||||||
'Личный сайт-визитка: резюме, проекты и мультиплеерный клон BattleCity. Тема — Матрица.',
|
'Личный сайт-визитка: резюме, проекты и мультиплеерный клон BattleCity. Тема — Матрица.',
|
||||||
tags: ['HTML/CSS/JS', 'C++', 'uWebSockets', 'Quintus.js', 'nginx', 'Docker'],
|
tags: ['HTML/CSS/JS', 'C++', 'uWebSockets', 'Quintus.js', 'nginx', 'Docker'],
|
||||||
@@ -31,7 +29,6 @@ const DATA = {
|
|||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'home_automatization',
|
name: 'home_automatization',
|
||||||
title: 'Home Automation Platform',
|
|
||||||
description:
|
description:
|
||||||
'A microservice smart-home platform on Go and Laravel. First zone is a grow box (watering, lighting, climate), the whole house comes next.',
|
'A microservice smart-home platform on Go and Laravel. First zone is a grow box (watering, lighting, climate), the whole house comes next.',
|
||||||
tags: ['Go', 'Laravel', 'Docker', 'RabbitMQ', 'ClickHouse', 'gRPC', 'MQTT'],
|
tags: ['Go', 'Laravel', 'Docker', 'RabbitMQ', 'ClickHouse', 'gRPC', 'MQTT'],
|
||||||
@@ -40,7 +37,6 @@ const DATA = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cactoz.su',
|
name: 'cactoz.su',
|
||||||
title: 'cactoz.su — this site',
|
|
||||||
description:
|
description:
|
||||||
'A personal portfolio site: résumé, projects and a multiplayer BattleCity clone. Matrix theme throughout.',
|
'A personal portfolio site: résumé, projects and a multiplayer BattleCity clone. Matrix theme throughout.',
|
||||||
tags: ['HTML/CSS/JS', 'C++', 'uWebSockets', 'Quintus.js', 'nginx', 'Docker'],
|
tags: ['HTML/CSS/JS', 'C++', 'uWebSockets', 'Quintus.js', 'nginx', 'Docker'],
|
||||||
@@ -66,15 +62,14 @@ function render() {
|
|||||||
.map(
|
.map(
|
||||||
(p) => `
|
(p) => `
|
||||||
<li class="project-card">
|
<li class="project-card">
|
||||||
<div class="project-card__bar"><span></span><span class="project-card__perms">drwxr-xr-x</span> ${p.name}</div>
|
<div class="project-card__header">
|
||||||
<div class="project-card__body">
|
<div class="project-card__name"><span class="project-card__perms">drwxr-xr-x</span> ${p.name}</div>
|
||||||
<h3 class="project-card__title">${p.title}</h3>
|
<a href="${p.url}" target="_blank" rel="noopener">${p.linkLabel} →</a>
|
||||||
<p class="project-card__desc">${p.description}</p>
|
|
||||||
<ul class="tag-list">
|
|
||||||
${p.tags.map((tag) => `<li class="tag">${tag}</li>`).join('')}
|
|
||||||
</ul>
|
|
||||||
<a class="project-card__link" href="${p.url}" target="_blank" rel="noopener">${p.linkLabel}: ${p.url.replace('https://', '')}</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="project-card__desc">${p.description}</p>
|
||||||
|
<ul class="tag-list">
|
||||||
|
${p.tags.map((tag) => `<li class="tag">${tag}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
</li>`
|
</li>`
|
||||||
)
|
)
|
||||||
.join('');
|
.join('');
|
||||||
|
|||||||
+7
-11
@@ -178,9 +178,10 @@ function renderExperience(list) {
|
|||||||
.map(
|
.map(
|
||||||
(job) => `
|
(job) => `
|
||||||
<li class="timeline-item">
|
<li class="timeline-item">
|
||||||
<div class="timeline-item__meta">${job.period} · ${job.duration} · ${job.location}</div>
|
<div class="timeline-item__meta">${job.period} · ${job.duration}</div>
|
||||||
<h3 class="timeline-item__role">${job.role} — ${job.company}</h3>
|
<h3 class="timeline-item__role">${job.role}</h3>
|
||||||
<ul>${job.bullets.map((b) => `<li>${b}</li>`).join('')}</ul>
|
<div class="timeline-item__company">${job.company} · ${job.location}</div>
|
||||||
|
<ul>${job.bullets.map((b) => `<li><span class="dash">-</span><span>${b}</span></li>`).join('')}</ul>
|
||||||
</li>`
|
</li>`
|
||||||
)
|
)
|
||||||
.join('');
|
.join('');
|
||||||
@@ -229,12 +230,7 @@ function render() {
|
|||||||
|
|
||||||
<section class="about">
|
<section class="about">
|
||||||
<h2 class="section-heading"><span class="section-heading__num">01</span>${t.aboutHeading}</h2>
|
<h2 class="section-heading"><span class="section-heading__num">01</span>${t.aboutHeading}</h2>
|
||||||
<div class="term-window">
|
${t.about.map((p) => `<p>${p}</p>`).join('')}
|
||||||
<div class="term-window__bar"><span></span><span></span><span></span></div>
|
|
||||||
<div class="term-window__body">
|
|
||||||
${t.about.map((p) => `<p>${p}</p>`).join('')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="experience">
|
<section class="experience">
|
||||||
@@ -255,8 +251,8 @@ function render() {
|
|||||||
<section class="contact">
|
<section class="contact">
|
||||||
<h2 class="section-heading"><span class="section-heading__num">05</span>${t.contactHeading}</h2>
|
<h2 class="section-heading"><span class="section-heading__num">05</span>${t.contactHeading}</h2>
|
||||||
<ul class="contact-list">
|
<ul class="contact-list">
|
||||||
<li><a href="mailto:${t.contact.email}">${t.contact.email}</a></li>
|
<li><span class="contact-list__label">email</span><a href="mailto:${t.contact.email}">${t.contact.email}</a></li>
|
||||||
<li><a href="${t.contact.telegramUrl}">${t.contact.telegram}</a></li>
|
<li><span class="contact-list__label">telegram</span><a href="${t.contact.telegramUrl}">${t.contact.telegram}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>`;
|
</section>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
<title>ls projects/ — cacto</title>
|
<title>ls projects/ — cacto</title>
|
||||||
<meta name="description" content="Проекты Гаммеля Дмитрия: home automation platform и этот сайт." />
|
<meta name="description" content="Проекты Гаммеля Дмитрия: home automation platform и этот сайт." />
|
||||||
<link rel="icon" type="image/svg+xml" href="/assets/cactus-logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/assets/cactus-logo.svg" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="/css/theme.css" />
|
<link rel="stylesheet" href="/css/theme.css" />
|
||||||
<link rel="stylesheet" href="/css/projects.css" />
|
<link rel="stylesheet" href="/css/projects.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ server {
|
|||||||
add_header X-Content-Type-Options nosniff always;
|
add_header X-Content-Type-Options nosniff always;
|
||||||
add_header X-Frame-Options DENY always;
|
add_header X-Frame-Options DENY always;
|
||||||
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
||||||
add_header Content-Security-Policy "default-src 'self'; connect-src 'self' wss://cactoz.su wss://www.cactoz.su; img-src 'self' data:; style-src 'self' 'unsafe-inline'; base-uri 'none'; frame-ancestors 'none'" always;
|
add_header Content-Security-Policy "default-src 'self'; connect-src 'self' wss://cactoz.su wss://www.cactoz.su; img-src 'self' data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; base-uri 'none'; frame-ancestors 'none'" always;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri.html $uri/ =404;
|
try_files $uri $uri.html $uri/ =404;
|
||||||
|
|||||||
Reference in New Issue
Block a user