Publish RedGifs 0.6.23 and migrate project website to Gitea

This commit is contained in:
DepressedCat committed 2026-10-11 01:03:40 +03:00
1 parent 01e0debd76
commit 580fd4f5b9
38 files changed
+5987 -42

No files matched your search

+979
View File
@@ -0,0 +1,979 @@
/* ==========================================================================
RedGifs Video Downloader — showcase site
Palette mirrors the extension UI (popup.css / content.js)
========================================================================== */
:root {
--bg: #070a0f;
--bg-soft: #0c1017;
--surface: #101623;
--surface-2: #131b29;
--line: rgba(255, 255, 255, 0.08);
--line-strong: rgba(255, 255, 255, 0.14);
--text: #f4f7fb;
--muted: #a3aec1;
--dim: #748096;
--pink: #f04468;
--pink-soft: #ff7895;
--pink-deep: #bf254c;
--green: #20b879;
--green-soft: #35d596;
--orange: #ff9c45;
--purple: #a66cff;
--cyan: #31d7ff;
--radius-s: 10px;
--radius: 16px;
--radius-l: 22px;
--shadow: 0 18px 46px rgba(0, 0, 0, 0.45);
--shadow-pink: 0 14px 38px rgba(240, 68, 104, 0.28);
--maxw: 1160px;
--ease: cubic-bezier(0.22, 1, 0.36, 1);
color-scheme: dark;
}
* { box-sizing: border-box; }
html {
scroll-behavior: smooth;
scroll-padding-top: 88px;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 16px;
line-height: 1.62;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
/* Ambient background glow */
body::before {
content: "";
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
background:
radial-gradient(900px 520px at 78% -8%, rgba(240, 68, 104, 0.20), transparent 62%),
radial-gradient(760px 480px at 6% 8%, rgba(166, 108, 255, 0.12), transparent 60%),
radial-gradient(900px 620px at 50% 108%, rgba(32, 184, 121, 0.08), transparent 62%),
linear-gradient(178deg, #0b0f17 0%, #070a0f 68%);
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4 {
margin: 0;
line-height: 1.18;
letter-spacing: -0.02em;
font-weight: 800;
}
p { margin: 0; }
code, kbd, .mono {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas,
"Liberation Mono", monospace;
font-size: 0.88em;
}
:focus-visible {
outline: 2px solid var(--pink-soft);
outline-offset: 3px;
border-radius: 6px;
}
.wrap {
width: 100%;
max-width: var(--maxw);
margin-inline: auto;
padding-inline: 22px;
}
.visually-hidden {
position: absolute;
width: 1px; height: 1px;
margin: -1px; padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
/* ============================ Skip link ============================ */
.skip-link {
position: absolute;
left: 50%;
top: -60px;
transform: translateX(-50%);
z-index: 200;
padding: 10px 18px;
border-radius: 0 0 12px 12px;
background: var(--pink);
color: #fff;
font-weight: 700;
transition: top 160ms var(--ease);
}
.skip-link:focus { top: 0; }
/* ============================ Header ============================ */
.site-header {
position: sticky;
top: 0;
z-index: 100;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
background: rgba(8, 11, 17, 0.72);
border-bottom: 1px solid transparent;
transition: border-color 220ms ease, background 220ms ease;
}
.site-header.is-stuck {
border-bottom-color: var(--line);
background: rgba(8, 11, 17, 0.92);
}
.site-header .wrap {
display: flex;
align-items: center;
gap: 18px;
height: 68px;
position: relative;
}
.brand {
display: flex;
align-items: center;
gap: 11px;
font-weight: 800;
letter-spacing: -0.015em;
flex: 0 0 auto;
}
.brand img { width: 32px; height: 32px; border-radius: 9px; }
.brand span { font-size: 15px; white-space: nowrap; }
.brand small {
display: block;
font-size: 10.5px;
font-weight: 600;
color: var(--dim);
letter-spacing: 0.06em;
text-transform: uppercase;
line-height: 1.2;
}
.nav {
display: flex;
align-items: center;
gap: 4px;
margin-inline-start: auto;
}
.nav a {
padding: 8px 12px;
border-radius: 9px;
font-size: 14px;
font-weight: 600;
color: var(--muted);
transition: color 150ms ease, background 150ms ease;
}
.nav a:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }
.header-actions {
display: flex;
align-items: center;
gap: 10px;
flex: 0 0 auto;
}
.lang-switch {
display: inline-flex;
padding: 3px;
border: 1px solid var(--line);
border-radius: 999px;
background: rgba(255, 255, 255, 0.04);
}
.lang-switch button {
padding: 5px 11px;
border: 0;
border-radius: 999px;
background: transparent;
color: var(--dim);
font: inherit;
font-size: 12px;
font-weight: 800;
letter-spacing: 0.04em;
cursor: pointer;
transition: color 150ms ease, background 150ms ease;
}
.lang-switch button:hover { color: var(--text); }
.lang-switch button[aria-pressed="true"] {
background: linear-gradient(135deg, var(--pink), var(--pink-deep));
color: #fff;
box-shadow: 0 4px 14px rgba(240, 68, 104, 0.34);
}
.nav-toggle {
display: none;
width: 40px; height: 40px;
place-items: center;
border: 1px solid var(--line);
border-radius: 11px;
background: rgba(255, 255, 255, 0.04);
color: var(--text);
cursor: pointer;
}
.nav-toggle svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
/* ============================ Buttons ============================ */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 9px;
padding: 12px 20px;
border: 1px solid transparent;
border-radius: 13px;
font-size: 14.5px;
font-weight: 700;
letter-spacing: -0.01em;
cursor: pointer;
white-space: nowrap;
transition: transform 160ms var(--ease), box-shadow 160ms ease,
filter 160ms ease, background 160ms ease, border-color 160ms ease;
}
.btn svg { width: 18px; height: 18px; flex: 0 0 auto; }
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn-primary {
background: linear-gradient(135deg, #ff5a7c 0%, var(--pink) 52%, var(--pink-deep) 100%);
color: #fff;
box-shadow: var(--shadow-pink);
}
.btn-primary:hover { filter: brightness(1.08); box-shadow: 0 18px 44px rgba(240, 68, 104, 0.4); }
.btn-ghost {
background: rgba(255, 255, 255, 0.05);
border-color: var(--line-strong);
color: var(--text);
}
.btn-ghost:hover { background: rgba(255, 255, 255, 0.09); border-color: rgba(255, 255, 255, 0.24); }
.btn-sm { padding: 9px 15px; font-size: 13px; border-radius: 11px; }
/* ============================ Section shell ============================ */
.section { padding: 96px 0; position: relative; }
.section-tight { padding: 68px 0; }
.section-head {
max-width: 720px;
margin-bottom: 46px;
}
.section-head.center { margin-inline: auto; text-align: center; }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
padding: 6px 13px;
border: 1px solid rgba(240, 68, 104, 0.34);
border-radius: 999px;
background: rgba(240, 68, 104, 0.10);
color: var(--pink-soft);
font-size: 11.5px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.eyebrow::before {
content: "";
width: 6px; height: 6px;
border-radius: 50%;
background: var(--pink);
box-shadow: 0 0 10px var(--pink);
}
.section h2 {
font-size: clamp(27px, 3.6vw, 40px);
margin-bottom: 14px;
}
.section-head p {
color: var(--muted);
font-size: 16.5px;
}
.grad-text {
background: linear-gradient(100deg, #ff8aa3 0%, var(--pink) 46%, #ff5a7c 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* ============================ Hero ============================ */
.hero { padding: 76px 0 84px; position: relative; }
.hero-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
gap: 56px;
align-items: center;
}
.hero-badges {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 22px;
}
.pill {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 6px 12px;
border: 1px solid var(--line);
border-radius: 999px;
background: rgba(255, 255, 255, 0.04);
color: var(--muted);
font-size: 12px;
font-weight: 700;
}
.pill svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.pill-pink { border-color: rgba(240, 68, 104, 0.4); background: rgba(240, 68, 104, 0.12); color: var(--pink-soft); }
.pill-green { border-color: rgba(32, 184, 121, 0.4); background: rgba(32, 184, 121, 0.12); color: var(--green-soft); }
.hero h1 {
font-size: clamp(38px, 6.2vw, 66px);
letter-spacing: -0.035em;
margin-bottom: 20px;
}
.hero h1 .line { display: block; }
.hero-lead {
max-width: 540px;
color: var(--muted);
font-size: 17.5px;
margin-bottom: 30px;
}
.hero-cta {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 26px;
}
.hero-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 18px;
color: var(--dim);
font-size: 13px;
}
.hero-meta strong { color: var(--muted); font-weight: 700; }
/* Hero art */
.hero-art { position: relative; }
.hero-art-frame {
position: relative;
border: 1px solid var(--line-strong);
border-radius: var(--radius-l);
overflow: hidden;
background: var(--surface);
box-shadow: var(--shadow), 0 0 0 1px rgba(240, 68, 104, 0.10);
}
.hero-art-frame img { width: 100%; height: auto; }
.hero-glow {
position: absolute;
inset: 8% 12% 4%;
z-index: -1;
border-radius: 50%;
background: radial-gradient(closest-side, rgba(240, 68, 104, 0.34), transparent 78%);
filter: blur(52px);
}
/* ============================ Stats strip ============================ */
.stats {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
border: 1px solid var(--line);
border-radius: var(--radius);
overflow: hidden;
background: var(--line);
}
.stat {
padding: 26px 22px;
background: rgba(13, 18, 27, 0.9);
text-align: center;
}
.stat b {
display: block;
font-size: clamp(24px, 3vw, 32px);
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(140deg, #fff, #b9c3d4);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.stat span { display: block; margin-top: 4px; font-size: 13px; color: var(--dim); }
/* ============================ Feature cards ============================ */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
gap: 18px;
}
.card {
position: relative;
padding: 26px 24px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: linear-gradient(168deg, rgba(19, 27, 41, 0.9), rgba(11, 15, 23, 0.9));
transition: transform 240ms var(--ease), border-color 240ms ease, box-shadow 240ms ease;
overflow: hidden;
}
.card::before {
content: "";
position: absolute;
inset-inline: 0;
top: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(240, 68, 104, 0.6), transparent);
opacity: 0;
transition: opacity 240ms ease;
}
.card:hover {
transform: translateY(-4px);
border-color: rgba(240, 68, 104, 0.34);
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.42);
}
.card:hover::before { opacity: 1; }
.card-icon {
display: grid;
place-items: center;
width: 44px; height: 44px;
margin-bottom: 16px;
border: 1px solid rgba(240, 68, 104, 0.32);
border-radius: 12px;
background: linear-gradient(140deg, rgba(240, 68, 104, 0.24), rgba(191, 37, 76, 0.14));
color: var(--pink-soft);
}
.card-icon svg {
width: 21px; height: 21px;
fill: none;
stroke: currentColor;
stroke-width: 1.9;
stroke-linecap: round;
stroke-linejoin: round;
}
.card-icon.green { border-color: rgba(32,184,121,.32); background: linear-gradient(140deg, rgba(32,184,121,.22), rgba(21,150,101,.12)); color: var(--green-soft); }
.card-icon.purple { border-color: rgba(166,108,255,.32); background: linear-gradient(140deg, rgba(166,108,255,.22), rgba(120,70,200,.12)); color: #c3a0ff; }
.card-icon.cyan { border-color: rgba(49,215,255,.3); background: linear-gradient(140deg, rgba(49,215,255,.2), rgba(20,120,160,.12)); color: #7fe4ff; }
.card-icon.orange { border-color: rgba(255,156,69,.32); background: linear-gradient(140deg, rgba(255,156,69,.22), rgba(232,107,33,.12)); color: var(--orange); }
.card h3 { font-size: 16.5px; margin-bottom: 8px; }
.card p { color: var(--muted); font-size: 14.5px; line-height: 1.6; }
/* ============================ Split / showcase ============================ */
.split {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 52px;
align-items: center;
}
.split.reverse .split-media { order: -1; }
.checklist { display: grid; gap: 14px; margin: 26px 0 0; padding: 0; }
.checklist li {
display: flex;
gap: 12px;
list-style: none;
color: var(--muted);
font-size: 15px;
}
.check-mark {
display: grid;
place-items: center;
flex: 0 0 22px;
width: 22px; height: 22px;
margin-top: 2px;
border-radius: 7px;
background: rgba(32, 184, 121, 0.16);
color: var(--green-soft);
}
.check-mark svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.checklist strong { color: var(--text); font-weight: 700; }
/* ============================ Popup mock ============================ */
/* Flow layout, not absolute chips: the chips sit above and below the card so
they can never cover it, at any column width. */
.mock-stage {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
padding: 28px 20px;
border: 1px solid var(--line);
border-radius: var(--radius-l);
background:
radial-gradient(circle at 82% 4%, rgba(240, 68, 104, 0.16), transparent 46%),
linear-gradient(160deg, rgba(19, 27, 41, 0.8), rgba(9, 12, 18, 0.9));
overflow: hidden;
}
.mock-stage::before {
content: "";
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(255,255,255,.028) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.028) 1px, transparent 1px);
background-size: 34px 34px;
mask-image: radial-gradient(circle at 50% 40%, #000 30%, transparent 76%);
-webkit-mask-image: radial-gradient(circle at 50% 40%, #000 30%, transparent 76%);
}
.popup-mock {
position: relative;
width: 100%;
max-width: 372px;
padding: 18px;
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 18px;
background:
radial-gradient(circle at 100% 0, rgba(255, 69, 103, 0.2), transparent 38%),
linear-gradient(155deg, #121824 0%, #090c12 72%);
box-shadow: var(--shadow);
}
.mock-head { display: flex; align-items: center; gap: 11px; margin-bottom: 17px; }
.mock-mark {
display: grid;
place-items: center;
width: 40px; height: 40px;
flex: 0 0 40px;
border-radius: 12px;
background: linear-gradient(135deg, #ff5a7c, var(--pink-deep));
color: #fff;
font-size: 14px;
font-weight: 800;
letter-spacing: 0.02em;
box-shadow: 0 7px 20px rgba(240, 68, 104, 0.34);
}
.mock-head h4 { font-size: 13.5px; }
.mock-head p { font-size: 10.5px; color: var(--dim); }
.mock-gear {
display: grid;
place-items: center;
width: 36px; height: 36px;
margin-inline-start: auto;
flex: 0 0 36px;
border: 1px solid rgba(255, 111, 142, 0.6);
border-radius: 11px;
background: linear-gradient(135deg, rgba(240, 68, 104, 0.34), rgba(191, 37, 76, 0.24));
color: #fff;
}
.mock-gear svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.mock-panel {
padding: 13px;
margin-bottom: 11px;
border: 1px solid rgba(240, 68, 104, 0.34);
border-radius: 13px;
background: rgba(15, 21, 31, 0.96);
}
.mock-label { display: block; margin-bottom: 6px; color: #c7cfdb; font-size: 10.5px; font-weight: 800; }
.mock-input {
padding: 8px 10px;
border: 1px solid #344157;
border-radius: 9px;
background: #0c1119;
color: var(--text);
font-size: 11px;
}
.mock-input.focus { border-color: var(--pink); box-shadow: 0 0 0 3px rgba(240, 68, 104, 0.13); }
.mock-check { display: flex; gap: 9px; align-items: flex-start; margin-top: 11px; }
.mock-box {
display: grid;
place-items: center;
flex: 0 0 15px;
width: 15px; height: 15px;
margin-top: 2px;
border: 1px solid #3c4a61;
border-radius: 5px;
background: #0c1119;
}
.mock-box.on { border-color: var(--pink); background: var(--pink); }
.mock-box svg { width: 9px; height: 9px; fill: none; stroke: #fff; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.mock-check strong { display: block; font-size: 11.5px; }
.mock-check small { display: block; color: var(--dim); font-size: 10px; line-height: 1.45; }
.mock-preview {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-top: 13px;
padding: 9px 11px;
border: 1px dashed rgba(255, 255, 255, 0.14);
border-radius: 10px;
font-size: 10.5px;
}
.mock-preview span { color: var(--dim); }
.mock-preview strong { color: var(--green-soft); font-weight: 700; }
.mock-progress { margin-top: 12px; padding: 11px 13px; border: 1px solid var(--line); border-radius: 12px; background: rgba(12, 17, 25, 0.9); }
.mock-progress-head { display: flex; justify-content: space-between; font-size: 11px; color: var(--muted); margin-bottom: 8px; }
.mock-progress-head b { color: var(--green-soft); }
.mock-track { height: 7px; border-radius: 999px; background: rgba(255, 255, 255, 0.08); overflow: hidden; }
.mock-bar {
height: 100%;
width: 68%;
border-radius: 999px;
background: linear-gradient(90deg, var(--green), var(--green-soft));
box-shadow: 0 0 14px rgba(53, 213, 150, 0.5);
animation: barPulse 2.6s var(--ease) infinite;
}
@keyframes barPulse {
0%, 100% { width: 62%; }
50% { width: 88%; }
}
.mock-foot { margin-top: 13px; color: var(--dim); font-size: 10.5px; text-align: center; }
/* Chips flanking the mock */
.mock-chip {
position: relative;
z-index: 1;
max-width: 100%;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 9px 13px;
border: 1px solid var(--line-strong);
border-radius: 12px;
background: rgba(14, 19, 29, 0.94);
box-shadow: var(--shadow);
font-size: 12px;
font-weight: 700;
backdrop-filter: blur(8px);
}
.mock-chip svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.mock-chip-a { align-self: flex-start; color: var(--green-soft); border-color: rgba(32,184,121,.4); animation: float1 7s ease-in-out infinite; }
.mock-chip-b { align-self: flex-end; color: var(--orange); border-color: rgba(255,156,69,.4); animation: float2 8s ease-in-out infinite; }
/* Small drift only — the gap in .mock-stage absorbs it without touching the card. */
@keyframes float1 { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes float2 { 0%,100% { transform: translateY(0); } 50% { transform: translateY(5px); } }
/* ============================ Steps ============================ */
.steps {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
gap: 18px;
}
.step {
position: relative;
padding: 26px 22px 24px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: rgba(13, 18, 27, 0.72);
transition: border-color 220ms ease, transform 220ms var(--ease);
}
.step:hover { border-color: rgba(240, 68, 104, 0.3); transform: translateY(-3px); }
.step-num {
display: grid;
place-items: center;
width: 34px; height: 34px;
margin-bottom: 15px;
border-radius: 11px;
background: linear-gradient(135deg, var(--pink), var(--pink-deep));
color: #fff;
font-size: 14px;
font-weight: 800;
box-shadow: 0 6px 18px rgba(240, 68, 104, 0.3);
}
.step h3 { font-size: 15.5px; margin-bottom: 7px; }
.step p { color: var(--muted); font-size: 14px; }
.step code {
display: inline-block;
margin-top: 9px;
padding: 4px 9px;
border: 1px solid var(--line);
border-radius: 7px;
background: rgba(0, 0, 0, 0.35);
color: var(--pink-soft);
}
/* ============================ Formats ============================ */
.formats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 14px;
margin-bottom: 22px;
}
.format {
padding: 20px 18px;
border: 1px solid var(--line);
border-radius: 14px;
background: rgba(13, 18, 27, 0.72);
text-align: center;
transition: border-color 200ms ease, background 200ms ease;
}
.format:hover { border-color: rgba(240, 68, 104, 0.34); background: rgba(240, 68, 104, 0.06); }
.format b { display: block; font-size: 17px; letter-spacing: -0.01em; }
.format span { display: block; margin-top: 5px; color: var(--dim); font-size: 12.5px; }
.notice {
display: flex;
gap: 14px;
padding: 18px 20px;
border: 1px solid rgba(255, 156, 69, 0.3);
border-radius: 14px;
background: rgba(255, 156, 69, 0.07);
}
.notice.legal { border-color: rgba(240, 68, 104, 0.34); background: rgba(240, 68, 104, 0.07); }
.notice.legal .notice-icon { color: var(--pink-soft); }
.notice-icon { flex: 0 0 22px; color: var(--orange); }
.notice-icon svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.notice h4 { font-size: 14.5px; margin-bottom: 5px; }
.notice p { color: var(--muted); font-size: 14px; }
/* ============================ Table ============================ */
.table-wrap {
border: 1px solid var(--line);
border-radius: var(--radius);
overflow-x: auto;
background: rgba(13, 18, 27, 0.72);
}
/* min-width keeps the two columns readable; it must stay under the width of the
split column on desktop (~530px) so the table does not scroll needlessly. */
table { width: 100%; border-collapse: collapse; min-width: 430px; }
th, td { padding: 13px 16px; text-align: left; border-bottom: 1px solid var(--line); font-size: 14px; }
th { color: var(--dim); font-size: 11.5px; font-weight: 800; letter-spacing: 0.09em; text-transform: uppercase; background: rgba(255,255,255,.02); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background 160ms ease; }
tbody tr:hover { background: rgba(255, 255, 255, 0.025); }
td code { color: var(--pink-soft); background: rgba(240, 68, 104, 0.1); padding: 3px 8px; border-radius: 6px; white-space: nowrap; }
td:last-child { color: var(--muted); }
/* ============================ FAQ ============================ */
.faq { display: grid; gap: 12px; max-width: 860px; margin-inline: auto; }
.faq details {
border: 1px solid var(--line);
border-radius: 14px;
background: rgba(13, 18, 27, 0.72);
transition: border-color 200ms ease, background 200ms ease;
overflow: hidden;
}
.faq details[open] { border-color: rgba(240, 68, 104, 0.3); background: rgba(19, 27, 41, 0.8); }
.faq summary {
display: flex;
align-items: center;
gap: 14px;
padding: 18px 22px;
font-size: 15.5px;
font-weight: 700;
cursor: pointer;
list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
content: "";
width: 9px; height: 9px;
margin-inline-start: auto;
flex: 0 0 9px;
border-right: 2px solid var(--pink-soft);
border-bottom: 2px solid var(--pink-soft);
transform: rotate(45deg) translate(-2px, -2px);
transition: transform 240ms var(--ease);
}
.faq details[open] summary::after { transform: rotate(-135deg) translate(-3px, -3px); }
.faq .faq-body { padding: 0 22px 20px; color: var(--muted); font-size: 14.5px; }
.faq .faq-body p + p { margin-top: 10px; }
/* ============================ CTA ============================ */
.cta-box {
position: relative;
padding: 56px 44px;
border: 1px solid rgba(240, 68, 104, 0.3);
border-radius: var(--radius-l);
background:
radial-gradient(circle at 15% 0%, rgba(240, 68, 104, 0.22), transparent 54%),
radial-gradient(circle at 92% 100%, rgba(166, 108, 255, 0.16), transparent 52%),
linear-gradient(160deg, rgba(19, 27, 41, 0.94), rgba(9, 12, 18, 0.94));
text-align: center;
overflow: hidden;
}
.cta-box h2 { font-size: clamp(26px, 3.4vw, 38px); margin-bottom: 14px; }
.cta-box p { max-width: 560px; margin: 0 auto 28px; color: var(--muted); font-size: 16px; }
.cta-actions { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
.cta-note { margin-top: 20px; font-size: 13px; color: var(--dim); }
/* ============================ Footer ============================ */
.site-footer {
border-top: 1px solid var(--line);
padding: 52px 0 34px;
background: rgba(6, 9, 13, 0.6);
}
.footer-grid {
display: grid;
grid-template-columns: minmax(0, 1.5fr) repeat(2, minmax(0, 1fr));
gap: 40px;
margin-bottom: 36px;
}
.footer-brand p { margin-top: 14px; max-width: 360px; color: var(--dim); font-size: 13.5px; }
.footer-col h5 {
margin: 0 0 14px;
color: var(--dim);
font-size: 11.5px;
font-weight: 800;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.footer-col a { color: var(--muted); font-size: 14px; transition: color 150ms ease; }
.footer-col a:hover { color: var(--pink-soft); }
.footer-bottom {
display: flex;
flex-wrap: wrap;
gap: 12px 24px;
justify-content: space-between;
align-items: center;
padding-top: 26px;
border-top: 1px solid var(--line);
color: var(--dim);
font-size: 12.5px;
}
.footer-bottom a { color: var(--muted); }
.footer-bottom a:hover { color: var(--pink-soft); }
/* ============================ Reveal animation ============================ */
.reveal {
opacity: 0;
transform: translateY(22px);
transition: opacity 620ms var(--ease), transform 620ms var(--ease);
}
.reveal.is-visible { opacity: 1; transform: none; }
/* ============================ Legal / doc pages ============================ */
.doc { padding: 56px 0 96px; }
.doc-inner { max-width: 800px; margin-inline: auto; }
.doc h1 { font-size: clamp(30px, 4.4vw, 44px); margin-bottom: 12px; }
.doc .doc-meta { color: var(--dim); font-size: 13.5px; margin-bottom: 36px; }
.doc h2 { font-size: 21px; margin: 38px 0 12px; }
.doc p { color: var(--muted); margin-bottom: 14px; }
.doc ul { color: var(--muted); padding-inline-start: 22px; margin: 0 0 16px; display: grid; gap: 8px; }
.doc a { color: var(--pink-soft); text-decoration: underline; text-underline-offset: 3px; }
.doc code { color: var(--pink-soft); background: rgba(240, 68, 104, 0.1); padding: 2px 7px; border-radius: 6px; }
.back-link { display: inline-flex; align-items: center; gap: 8px; margin-bottom: 26px; color: var(--muted); font-size: 14px; font-weight: 600; }
.back-link:hover { color: var(--pink-soft); }
.back-link svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* 404 */
.notfound { display: grid; place-items: center; min-height: 66vh; text-align: center; padding: 60px 0; }
.notfound b {
display: block;
font-size: clamp(72px, 14vw, 140px);
font-weight: 800;
letter-spacing: -0.05em;
line-height: 1;
background: linear-gradient(140deg, #ff8aa3, var(--pink) 55%, var(--pink-deep));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.notfound h1 { font-size: clamp(24px, 3.4vw, 34px); margin: 14px 0 10px; }
.notfound p { color: var(--muted); margin-bottom: 26px; }
/* ============================ Responsive ============================ */
@media (max-width: 980px) {
.hero-grid { grid-template-columns: 1fr; gap: 44px; }
.hero-lead { max-width: none; }
.split { grid-template-columns: 1fr; gap: 36px; }
.split.reverse .split-media { order: 0; }
.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.footer-grid { grid-template-columns: 1fr 1fr; gap: 32px; }
.footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 760px) {
.nav-toggle { display: grid; }
.nav {
position: absolute;
inset-inline: 0;
top: 64px;
flex-direction: column;
align-items: stretch;
gap: 2px;
padding: 10px;
border: 1px solid var(--line);
border-radius: 16px;
background: rgba(10, 14, 21, 0.98);
box-shadow: var(--shadow);
backdrop-filter: blur(18px);
}
.nav:not(.is-open) { display: none; }
.nav a { padding: 11px 14px; font-size: 15px; }
.header-actions { margin-inline-start: auto; }
.section { padding: 68px 0; }
.cta-box { padding: 40px 22px; }
.hero { padding: 48px 0 60px; }
}
@media (max-width: 460px) {
.brand span { font-size: 13.5px; }
.hero-cta { flex-direction: column; align-items: stretch; }
.hero-cta .btn { width: 100%; }
.stats { grid-template-columns: 1fr; }
.wrap { padding-inline: 16px; }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
.reveal { opacity: 1; transform: none; }
}
@media print {
body::before, .site-header, .hero-art, .cta-actions { display: none !important; }
body { background: #fff; color: #000; }
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="RedGifs Video Downloader">
<defs>
<linearGradient id="surface" x1="20" y1="15" x2="108" y2="116" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ff5a7c"/>
<stop offset="0.55" stop-color="#ee3e67"/>
<stop offset="1" stop-color="#c9234d"/>
</linearGradient>
<linearGradient id="shine" x1="31" y1="17" x2="81" y2="86" gradientUnits="userSpaceOnUse">
<stop stop-color="#ffffff" stop-opacity="0.24"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="150%">
<feDropShadow dx="0" dy="5" stdDeviation="5" flood-color="#350814" flood-opacity="0.36"/>
</filter>
</defs>
<rect x="7" y="5" width="114" height="114" rx="32" fill="url(#surface)" filter="url(#shadow)"/>
<rect x="8.5" y="6.5" width="111" height="111" rx="30.5" fill="none" stroke="#ff9caf" stroke-width="3" stroke-opacity="0.72"/>
<path d="M22 44C30 21 49 10 72 10c15 0 28 5 38 14C91 19 64 24 43 43c-10 9-17 20-22 32-3-10-3-21 1-31Z" fill="url(#shine)"/>
<g fill="none" stroke="#ffffff" stroke-width="10" stroke-linecap="round" stroke-linejoin="round">
<path d="M64 30v48"/>
<path d="m45 61 19 19 19-19"/>
<path d="M39 96h50"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

+252
View File
@@ -0,0 +1,252 @@
/* ==========================================================================
RedGifs Video Downloader — showcase site behaviour
No dependencies, no network calls, no tracking.
========================================================================== */
(function () {
'use strict';
var STORAGE_KEY = 'rgvd-lang';
/* ------------------------------------------------------- repository --
ONE place to point the site at the public download location.
While the release repository is private, every link below 404s for
visitors who are not signed in as its owner. Change REPO_URL once and
all nine links follow: data-link="release" -> latest release,
data-link="repo" -> repository root, data-link="license" -> LICENSE.
The hrefs stay in the HTML as well, so the links still work with
JavaScript disabled; this only rewrites them when REPO_URL differs.
-------------------------------------------------------------------------- */
var REPO_URL = 'https://git.lonarpgproxy.space/OwnerLink/redgifs-video-downloader';
function initLinks() {
var targets = {
repo: REPO_URL,
release: REPO_URL + '/releases/latest',
license: REPO_URL + '/src/branch/main/LICENSE'
};
var nodes = document.querySelectorAll('[data-link]');
for (var i = 0; i < nodes.length; i++) {
var href = targets[nodes[i].getAttribute('data-link')];
if (href) nodes[i].setAttribute('href', href);
}
}
/* ---------------------------------------------------------------- i18n --
Russian is authored directly in the markup. English lives in the
data-en / data-en-alt / data-en-label attributes and is swapped in on
demand. The original Russian is cached in data-ru on first switch.
-------------------------------------------------------------------------- */
var PAGE_META = {
ru: {
title: document.title,
description: metaContent('description')
},
en: {
title: document.documentElement.getAttribute('data-en-title') || '',
description: document.documentElement.getAttribute('data-en-description') || ''
}
};
function metaContent(name) {
var el = document.querySelector('meta[name="' + name + '"]');
return el ? el.getAttribute('content') : '';
}
function setMetaContent(name, value) {
var el = document.querySelector('meta[name="' + name + '"]');
if (el && value) el.setAttribute('content', value);
}
function swapAttribute(el, enAttr, targetAttr, ruAttr, lang) {
var en = el.getAttribute(enAttr);
if (!en) return;
if (!el.hasAttribute(ruAttr)) {
el.setAttribute(ruAttr, el.getAttribute(targetAttr) || '');
}
el.setAttribute(targetAttr, lang === 'en' ? en : el.getAttribute(ruAttr));
}
function applyLanguage(lang) {
var isEn = lang === 'en';
document.documentElement.setAttribute('lang', isEn ? 'en' : 'ru');
document.documentElement.setAttribute('data-lang', lang);
// Text / markup content
var nodes = document.querySelectorAll('[data-en]');
for (var i = 0; i < nodes.length; i++) {
var el = nodes[i];
if (!el.hasAttribute('data-ru')) {
el.setAttribute('data-ru', el.innerHTML);
}
// Both sides are authored in this repository, never user input.
el.innerHTML = isEn ? el.getAttribute('data-en') : el.getAttribute('data-ru');
}
// Accessible names that are not text content
var alts = document.querySelectorAll('[data-en-alt]');
for (var a = 0; a < alts.length; a++) {
swapAttribute(alts[a], 'data-en-alt', 'alt', 'data-ru-alt', lang);
}
var labels = document.querySelectorAll('[data-en-label]');
for (var b = 0; b < labels.length; b++) {
swapAttribute(labels[b], 'data-en-label', 'aria-label', 'data-ru-label', lang);
}
// Document metadata
if (PAGE_META[lang] && PAGE_META[lang].title) {
document.title = PAGE_META[lang].title;
}
if (PAGE_META[lang] && PAGE_META[lang].description) {
setMetaContent('description', PAGE_META[lang].description);
}
// Switch buttons
var buttons = document.querySelectorAll('[data-set-lang]');
for (var c = 0; c < buttons.length; c++) {
buttons[c].setAttribute(
'aria-pressed',
buttons[c].getAttribute('data-set-lang') === lang ? 'true' : 'false'
);
}
}
function storedLanguage() {
var stored = null;
try {
stored = window.localStorage.getItem(STORAGE_KEY);
} catch (err) {
stored = null;
}
if (stored === 'ru' || stored === 'en') return stored;
var nav = (navigator.language || '').toLowerCase();
var list = navigator.languages || [];
for (var i = 0; i < list.length; i++) {
if (String(list[i]).toLowerCase().indexOf('ru') === 0) return 'ru';
}
return nav.indexOf('ru') === 0 ? 'ru' : 'en';
}
function initLanguage() {
var buttons = document.querySelectorAll('[data-set-lang]');
if (!buttons.length) return;
applyLanguage(storedLanguage());
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function () {
var lang = this.getAttribute('data-set-lang');
applyLanguage(lang);
try {
window.localStorage.setItem(STORAGE_KEY, lang);
} catch (err) {
/* private mode — the choice simply is not remembered */
}
});
}
}
/* ------------------------------------------------------------- header -- */
function initHeader() {
var header = document.getElementById('siteHeader');
if (!header) return;
var ticking = false;
function update() {
header.classList.toggle('is-stuck', window.scrollY > 8);
ticking = false;
}
window.addEventListener('scroll', function () {
if (!ticking) {
ticking = true;
window.requestAnimationFrame(update);
}
}, { passive: true });
update();
}
/* ---------------------------------------------------------- mobile nav -- */
function initNav() {
var toggle = document.getElementById('navToggle');
var nav = document.getElementById('primaryNav');
if (!toggle || !nav) return;
function close() {
nav.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
}
toggle.addEventListener('click', function () {
var open = nav.classList.toggle('is-open');
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
});
nav.addEventListener('click', function (event) {
if (event.target.closest('a')) close();
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape') close();
});
document.addEventListener('click', function (event) {
if (!nav.classList.contains('is-open')) return;
if (nav.contains(event.target) || toggle.contains(event.target)) return;
close();
});
}
/* ------------------------------------------------------------- reveal -- */
function initReveal() {
var items = document.querySelectorAll('.reveal');
if (!items.length) return;
var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduced || !('IntersectionObserver' in window)) {
for (var i = 0; i < items.length; i++) items[i].classList.add('is-visible');
return;
}
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
});
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });
for (var j = 0; j < items.length; j++) {
// Stagger siblings a little so grids cascade instead of popping.
var delay = (j % 4) * 70;
items[j].style.transitionDelay = delay + 'ms';
observer.observe(items[j]);
}
}
/* ---------------------------------------------------------------- boot -- */
function boot() {
initLinks();
initLanguage();
initHeader();
initNav();
initReveal();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();