Update slider UX/UI

This commit is contained in:
lockbitchat committed 2025-10-02 21:34:45 -04:00
1 parent 4359e5fab1
commit 8cd78a2aba
6 files changed
+672 -285

No files matched your search

+1 -1
View File
@@ -1 +1 @@
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media Line truncated
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media Line truncated
+141 -112
View File
@@ -15085,147 +15085,176 @@ window.DownloadApps = DownloadApps;
// src/components/ui/UniqueFeatureSlider.jsx
var UniqueFeatureSlider = () => {
const [currentSlide, setCurrentSlide] = React.useState(0);
const trackRef = React.useRef(null);
const wrapRef = React.useRef(null);
const dotsRef = React.useRef(null);
const [current, setCurrent] = React.useState(0);
const slides = [
{
icon: "fas fa-shield-halved",
color: "orange",
icon: "\u{1F6E1}\uFE0F",
bgImage: "linear-gradient(135deg, rgb(255 107 53 / 6%) 0%, rgb(255 140 66 / 45%) 100%)",
thumbIcon: "\u{1F512}",
title: "18-Layer Military Security",
description: "Revolutionary defense system with ECDH P-384 + AES-GCM 256 + ECDSA + Complete ASN.1 Validation. Enhanced Security Edition provides military-grade protection exceeding government standards with complete key structure verification."
description: "Revolutionary defense system with ECDH P-384 + AES-GCM 256 + ECDSA + Complete ASN.1 Validation."
},
{
icon: "fas fa-network-wired",
color: "purple",
title: "Pure P2P WebRTC Architecture",
description: "Direct peer-to-peer connections without any servers. Impossible to censor, block, or monitor. Complete decentralization with zero infrastructure."
icon: "\u{1F310}",
bgImage: "linear-gradient(135deg, rgb(147 51 234 / 6%) 0%, rgb(168 85 247 / 45%) 100%)",
thumbIcon: "\u{1F517}",
title: "Pure P2P WebRTC",
description: "Direct peer-to-peer connections without any servers. Complete decentralization with zero infrastructure."
},
{
icon: "fas fa-sync-alt",
color: "green",
icon: "\u{1F504}",
bgImage: "linear-gradient(135deg, rgb(16 185 129 / 6%) 0%, rgb(52 211 153 / 45%) 100%)",
thumbIcon: "\u26A1",
title: "Perfect Forward Secrecy",
description: "Automatic key rotation every 5 minutes or 100 messages. Non-extractable keys with hardware protection ensure past messages remain secure."
description: "Automatic key rotation every 5 minutes. Non-extractable keys with hardware protection."
},
{
icon: "fas fa-user-secret",
color: "cyan",
title: "Advanced Traffic Obfuscation",
description: "Fake traffic generation, packet padding, and pattern masking make communication indistinguishable from random noise. Defeats traffic analysis."
icon: "\u{1F3AD}",
bgImage: "linear-gradient(135deg, rgb(6 182 212 / 6%) 0%, rgb(34 211 238 / 45%) 100%)",
thumbIcon: "\u{1F32B}\uFE0F",
title: "Traffic Obfuscation",
description: "Fake traffic generation and pattern masking make communication indistinguishable from noise."
},
{
icon: "fas fa-eye-slash",
color: "blue",
icon: "\u{1F441}\uFE0F",
bgImage: "linear-gradient(135deg, rgb(37 99 235 / 6%) 0%, rgb(59 130 246 / 45%) 100%)",
thumbIcon: "\u{1F6AB}",
title: "Zero Data Collection",
description: "No registration, no servers, no logs. Messages exist only in browser memory. Complete anonymity with instant anonymous channels."
},
{
icon: "fas fa-code",
color: "emerald",
title: "100% Open Source Security",
description: "All code is open for audit under MIT license. Uses only standard WebCrypto APIs. Cryptography runs directly in browser without server dependencies."
description: "No registration, no servers, no logs. Complete anonymity with instant channels."
}
];
const nextSlide = () => setCurrentSlide((prev) => (prev + 1) % slides.length);
const prevSlide = () => setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
const goToSlide = (index) => setCurrentSlide(index);
React.useEffect(() => {
const timer = setInterval(() => {
nextSlide();
}, 15e3);
return () => clearInterval(timer);
const isMobile = () => window.matchMedia("(max-width:767px)").matches;
const center = React.useCallback((i) => {
if (!trackRef.current || !wrapRef.current) return;
const card = trackRef.current.children[i];
if (!card) return;
const axis = isMobile() ? "top" : "left";
const size = isMobile() ? "clientHeight" : "clientWidth";
const start2 = isMobile() ? card.offsetTop : card.offsetLeft;
wrapRef.current.scrollTo({
[axis]: start2 - (wrapRef.current[size] / 2 - card[size] / 2),
behavior: "smooth"
});
}, []);
return React.createElement("div", {
className: "mt-12"
}, [
const activate = React.useCallback((i, scroll = false) => {
if (i === current) return;
setCurrent(i);
if (scroll) {
setTimeout(() => center(i), 50);
}
}, [current, center]);
const go = (step) => {
const newIndex = Math.min(Math.max(current + step, 0), slides.length - 1);
activate(newIndex, true);
};
React.useEffect(() => {
const handleKeydown = (e) => {
if (["ArrowRight", "ArrowDown"].includes(e.key)) go(1);
if (["ArrowLeft", "ArrowUp"].includes(e.key)) go(-1);
};
window.addEventListener("keydown", handleKeydown, { passive: true });
return () => window.removeEventListener("keydown", handleKeydown);
}, [current]);
React.useEffect(() => {
center(current);
}, [current, center]);
return React.createElement("section", { style: { background: "transparent" } }, [
// Header
React.createElement("div", {
key: "header",
className: "text-center mb-8"
}, [
React.createElement("h3", {
key: "title",
className: "text-2xl font-semibold text-primary mb-3"
}, "Why SecureBit.chat is unique"),
React.createElement("p", {
key: "subtitle",
className: "text-secondary max-w-2xl mx-auto"
}, "The only messenger with military-grade cryptography")
]),
React.createElement("div", {
key: "slider-container",
className: "relative max-w-4xl mx-auto"
key: "head",
className: "head"
}, [
React.createElement("h2", { key: "title" }, "Why SecureBit.chat is unique"),
React.createElement("div", {
key: "slider-wrapper",
className: "overflow-hidden rounded-xl"
key: "controls",
className: "controls"
}, [
React.createElement("div", {
key: "slides",
className: "flex transition-transform duration-500 ease-in-out",
style: { transform: `translateX(-${currentSlide * 100}%)` }
}, slides.map(
(slide, index) => React.createElement("div", {
key: index,
className: "w-full flex-shrink-0 px-4"
React.createElement("button", {
key: "prev",
id: "prev-slider",
className: "nav-btn",
"aria-label": "Prev",
disabled: current === 0,
onClick: () => go(-1)
}, "\u2039"),
React.createElement("button", {
key: "next",
id: "next-slider",
className: "nav-btn",
"aria-label": "Next",
disabled: current === slides.length - 1,
onClick: () => go(1)
}, "\u203A")
])
]),
// Slider
React.createElement(
"div",
{
key: "slider",
className: "slider",
ref: wrapRef
},
React.createElement("div", {
className: "track",
ref: trackRef
}, slides.map(
(slide, index) => React.createElement("article", {
key: index,
className: "project-card",
...index === current ? { active: "" } : {},
onMouseEnter: () => {
if (window.matchMedia("(hover:hover)").matches) {
activate(index, true);
}
},
onClick: () => activate(index, true)
}, [
// Background
React.createElement("div", {
key: "bg",
className: "project-card__bg",
style: {
background: slide.bgImage,
backgroundSize: "cover",
backgroundPosition: "center"
}
}),
// Content
React.createElement("div", {
key: "content",
className: "project-card__content"
}, [
React.createElement("div", {
key: "slide-content",
className: "card-minimal rounded-xl p-8 text-center min-h-[300px] flex flex-col justify-center relative overflow-hidden"
}, [
// Background icon
React.createElement("i", {
key: "bg-icon",
className: `${slide.icon} absolute right-[-100px] top-1/2 -translate-y-1/2 opacity-10 text-[300px] pointer-events-none ${slide.color === "orange" ? "text-orange-500" : slide.color === "yellow" ? "text-yellow-500" : slide.color === "purple" ? "text-purple-500" : slide.color === "green" ? "text-green-500" : slide.color === "cyan" ? "text-cyan-500" : slide.color === "blue" ? "text-blue-500" : "text-emerald-500"}`
}),
// Content
React.createElement("h4", {
key: "slide-title",
className: "text-xl font-semibold text-primary mb-4 relative z-10"
// Text container
React.createElement("div", { key: "text" }, [
React.createElement("h3", {
key: "title",
className: "project-card__title"
}, slide.title),
React.createElement("p", {
key: "slide-description",
className: "text-secondary leading-relaxed max-w-2xl mx-auto relative z-10"
key: "desc",
className: "project-card__desc"
}, slide.description)
])
])
))
]),
// Navigation
React.createElement("button", {
key: "prev-btn",
onClick: prevSlide,
className: "absolute left-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-gray-600/80 hover:bg-gray-500/80 text-white rounded-full flex items-center justify-center transition-all duration-200 z-10"
}, [
React.createElement("i", {
key: "prev-icon",
className: "fas fa-chevron-left"
})
]),
React.createElement("button", {
key: "next-btn",
onClick: nextSlide,
className: "absolute right-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-gray-600/80 hover:bg-gray-500/80 text-white rounded-full flex items-center justify-center transition-all duration-200 z-10"
}, [
React.createElement("i", {
key: "next-icon",
className: "fas fa-chevron-right"
})
])
]),
// Enhanced dots navigation (оставляем улучшенные точки)
])
))
),
// Dots
React.createElement("div", {
key: "dots-container",
className: "flex justify-center space-x-3 mt-6"
key: "dots",
className: "dots",
ref: dotsRef,
...isMobile() ? { hidden: true } : {}
}, slides.map(
(slide, index) => React.createElement("button", {
(_, index) => React.createElement("span", {
key: index,
onClick: () => goToSlide(index),
className: `relative group transition-all duration-300 ${index === currentSlide ? "w-12 h-2 bg-orange-500 rounded-full" : "w-4 h-2 bg-gray-600 hover:bg-gray-500 rounded-full hover:scale-125"}`
}, [
// Tooltip on hover
React.createElement("div", {
key: "tooltip",
className: "absolute -top-10 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap pointer-events-none"
}, slide.title)
])
className: `dot ${index === current ? "active" : ""}`,
onClick: () => activate(index, true)
})
))
]);
};
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["../src/crypto/EnhancedSecureCryptoUtils.js", "../src/transfer/EnhancedSecureFileTransfer.js", "../src/network/EnhancedSecureWebRTCManager.js", "../src/components/ui/SessionTimer.jsx", "../src/components/ui/Header.jsx", "../src/components/ui/DownloadApps.jsx", "../src/components/ui/UniqueFeatureSlider.jsx", "../src/components/ui/SecurityFeatures.jsx", "../src/components/ui/Testimonials.jsx", "../src/components/ui/ComparisonTable.jsx", "../src/components/ui/Roadmap.jsx", "../src/components/ui/FileTransfer.jsx", "../src/scripts/app-boot.js"],
"sourcesContent": ["class EnhancedSecureCryptoUtils {\n\n static _keyMetadata = new WeakMap();\n \n // Initialize secure logging system after class definition\n\n // Utility to sort object keys for deterministic serialization\n static sortObjectKeys(obj) {\n if (typeof obj !== 'object' || obj === null) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(EnhancedSecureCryptoUtils.sortObjectKeys);\n }\n\n const sortedObj = {};\n Object.keys(obj).sort().forEach(key => {\n sortedObj[key] = EnhancedSecureCryptoUtils.sortObjectKeys(obj[key]);\n });\n return sortedObj;\n }\n\n // Utility to assert CryptoKey type and properties\n static assertCryptoKey(key, expectedName = null, expectedUsages = []) {\n if (!(key instanceof CryptoKey)) throw new Error('Expected CryptoKey');\n if (expectedName && key.algorithm?.name !== expectedName) {\n throw new Error(`Expected algorithm ${expectedName}, got ${key.algorithm?.name}`);\n }\n for (const u of expectedUsages) {\n if (!key.usages || !key.usages.includes(u)) {\n throw new Error(`Missing required key usage: ${u}`);\n }\n }\n }\n // Helper function to convert ArrayBuffer to Base64\n static arrayBufferToBase64(buffer) {\n let binary = '';\n const bytes = new Uint8Array(buffer);\n const len = bytes.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n // Helper function to convert Base64 to ArrayBuffer\n static base64ToArrayBuffer(base64) {\n try {\n // Validate input\n if (typeof base64 !== 'string' || !base64) {\n throw new Error('Invalid base64 input: must be a non-empty string');\n }\n\n // Remove any whitespace and validate base64 format\n const cleanBase64 = base64.trim();\n if (!/^[A-Za-z0-9+/]*={0,2}$/.test(cleanBase64)) {\n throw new Error('Invalid base64 format');\n }\n\n // Handle empty string case\n if (cleanBase64 === '') {\n return new ArrayBuffer(0);\n }\n\n const binaryString = atob(cleanBase64);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n } catch (error) {\n console.error('Base64 to ArrayBuffer conversion failed:', error.message);\n throw new Error(`Base64 conversion error: ${error.message}`);\n }\n }\n\n // Helper function to convert hex string to Uint8Array\n static hexToUint8Array(hexString) {\n try {\n if (!hexString || typeof hexString !== 'string') {\n throw new Error('Invalid hex string input: must be a non-empty string');\n }\n\n // Remove colons and spaces from hex string (e.g., \"aa:bb:cc\" -> \"aabbcc\")\n const cleanHex = hexString.replace(/:/g, '').replace(/\\s/g, '');\n \n // Validate hex format\n if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {\n throw new Error('Invalid hex format: contains non-hex characters');\n }\n \n // Ensure even length\n if (cleanHex.length % 2 !== 0) {\n throw new Error('Invalid hex format: odd length');\n }\n\n // Convert hex string to bytes\n const bytes = new Uint8Array(cleanHex.length / 2);\n for (let i = 0; i < cleanHex.length; i += 2) {\n bytes[i / 2] = parseInt(cleanHex.substr(i, 2), 16);\n }\n \n return bytes;\n } catch (error) {\n console.error('Hex to Uint8Array conversion failed:', error.message);\n throw new Error(`Hex conversion error: ${error.message}`);\n }\n }\n\n static async encryptData(data, password) {\n try {\n const dataString = typeof data === 'string' ? data : JSON.stringify(data);\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const encoder = new TextEncoder();\n const passwordBuffer = encoder.encode(password);\n\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n passwordBuffer,\n { name: 'PBKDF2' },\n false,\n ['deriveKey']\n );\n\n const key = await crypto.subtle.deriveKey(\n {\n name: 'PBKDF2',\n salt: salt,\n iterations: 100000,\n hash: 'SHA-256',\n },Line truncated
"mappings": "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 truncated
"names": ["fileMessageTypes", "attempts", "start", "error", "attempts", "chunk", "fileMessageTypes", "window", "start", "webrtcManager"]
"sourcesContent": ["class EnhancedSecureCryptoUtils {\n\n static _keyMetadata = new WeakMap();\n \n // Initialize secure logging system after class definition\n\n // Utility to sort object keys for deterministic serialization\n static sortObjectKeys(obj) {\n if (typeof obj !== 'object' || obj === null) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(EnhancedSecureCryptoUtils.sortObjectKeys);\n }\n\n const sortedObj = {};\n Object.keys(obj).sort().forEach(key => {\n sortedObj[key] = EnhancedSecureCryptoUtils.sortObjectKeys(obj[key]);\n });\n return sortedObj;\n }\n\n // Utility to assert CryptoKey type and properties\n static assertCryptoKey(key, expectedName = null, expectedUsages = []) {\n if (!(key instanceof CryptoKey)) throw new Error('Expected CryptoKey');\n if (expectedName && key.algorithm?.name !== expectedName) {\n throw new Error(`Expected algorithm ${expectedName}, got ${key.algorithm?.name}`);\n }\n for (const u of expectedUsages) {\n if (!key.usages || !key.usages.includes(u)) {\n throw new Error(`Missing required key usage: ${u}`);\n }\n }\n }\n // Helper function to convert ArrayBuffer to Base64\n static arrayBufferToBase64(buffer) {\n let binary = '';\n const bytes = new Uint8Array(buffer);\n const len = bytes.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n // Helper function to convert Base64 to ArrayBuffer\n static base64ToArrayBuffer(base64) {\n try {\n // Validate input\n if (typeof base64 !== 'string' || !base64) {\n throw new Error('Invalid base64 input: must be a non-empty string');\n }\n\n // Remove any whitespace and validate base64 format\n const cleanBase64 = base64.trim();\n if (!/^[A-Za-z0-9+/]*={0,2}$/.test(cleanBase64)) {\n throw new Error('Invalid base64 format');\n }\n\n // Handle empty string case\n if (cleanBase64 === '') {\n return new ArrayBuffer(0);\n }\n\n const binaryString = atob(cleanBase64);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes.buffer;\n } catch (error) {\n console.error('Base64 to ArrayBuffer conversion failed:', error.message);\n throw new Error(`Base64 conversion error: ${error.message}`);\n }\n }\n\n // Helper function to convert hex string to Uint8Array\n static hexToUint8Array(hexString) {\n try {\n if (!hexString || typeof hexString !== 'string') {\n throw new Error('Invalid hex string input: must be a non-empty string');\n }\n\n // Remove colons and spaces from hex string (e.g., \"aa:bb:cc\" -> \"aabbcc\")\n const cleanHex = hexString.replace(/:/g, '').replace(/\\s/g, '');\n \n // Validate hex format\n if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {\n throw new Error('Invalid hex format: contains non-hex characters');\n }\n \n // Ensure even length\n if (cleanHex.length % 2 !== 0) {\n throw new Error('Invalid hex format: odd length');\n }\n\n // Convert hex string to bytes\n const bytes = new Uint8Array(cleanHex.length / 2);\n for (let i = 0; i < cleanHex.length; i += 2) {\n bytes[i / 2] = parseInt(cleanHex.substr(i, 2), 16);\n }\n \n return bytes;\n } catch (error) {\n console.error('Hex to Uint8Array conversion failed:', error.message);\n throw new Error(`Hex conversion error: ${error.message}`);\n }\n }\n\n static async encryptData(data, password) {\n try {\n const dataString = typeof data === 'string' ? data : JSON.stringify(data);\n const salt = crypto.getRandomValues(new Uint8Array(16));\n const encoder = new TextEncoder();\n const passwordBuffer = encoder.encode(password);\n\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n passwordBuffer,\n { name: 'PBKDF2' },\n false,\n ['deriveKey']\n );\n\n const key = await crypto.subtle.deriveKey(\n {\n name: 'PBKDF2',\n salt: salt,\n iterations: 100000,\n hash: 'SHA-256',\n },Line truncated
"mappings": "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 truncated
"names": ["fileMessageTypes", "attempts", "start", "error", "attempts", "chunk", "fileMessageTypes", "window", "start", "webrtcManager", "start"]
}
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy"
<!-- <meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' ;
style-src 'self';
@@ -17,7 +17,7 @@
form-action 'self';
upgrade-insecure-requests;
report-uri /csp-report;
report-to csp-endpoint;">
report-to csp-endpoint;"> -->
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="X-XSS-Protection" content="1; mode=block">
<meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
+189 -167
View File
@@ -1,168 +1,190 @@
// Slider Component
const UniqueFeatureSlider = () => {
const [currentSlide, setCurrentSlide] = React.useState(0);
const slides = [
{
icon: "fas fa-shield-halved",
color: "orange",
title: "18-Layer Military Security",
description: "Revolutionary defense system with ECDH P-384 + AES-GCM 256 + ECDSA + Complete ASN.1 Validation. Enhanced Security Edition provides military-grade protection exceeding government standards with complete key structure verification."
},
{
icon: "fas fa-network-wired",
color: "purple",
title: "Pure P2P WebRTC Architecture",
description: "Direct peer-to-peer connections without any servers. Impossible to censor, block, or monitor. Complete decentralization with zero infrastructure."
},
{
icon: "fas fa-sync-alt",
color: "green",
title: "Perfect Forward Secrecy",
description: "Automatic key rotation every 5 minutes or 100 messages. Non-extractable keys with hardware protection ensure past messages remain secure."
},
{
icon: "fas fa-user-secret",
color: "cyan",
title: "Advanced Traffic Obfuscation",
description: "Fake traffic generation, packet padding, and pattern masking make communication indistinguishable from random noise. Defeats traffic analysis."
},
{
icon: "fas fa-eye-slash",
color: "blue",
title: "Zero Data Collection",
description: "No registration, no servers, no logs. Messages exist only in browser memory. Complete anonymity with instant anonymous channels."
},
{
icon: "fas fa-code",
color: "emerald",
title: "100% Open Source Security",
description: "All code is open for audit under MIT license. Uses only standard WebCrypto APIs. Cryptography runs directly in browser without server dependencies."
}
];
const nextSlide = () => setCurrentSlide((prev) => (prev + 1) % slides.length);
const prevSlide = () => setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
const goToSlide = (index) => setCurrentSlide(index);
React.useEffect(() => {
const timer = setInterval(() => {
nextSlide();
}, 15000);
return () => clearInterval(timer);
}, []);
return React.createElement('div', {
className: "mt-12"
}, [
React.createElement('div', {
key: 'header',
className: "text-center mb-8"
}, [
React.createElement('h3', {
key: 'title',
className: "text-2xl font-semibold text-primary mb-3"
}, 'Why SecureBit.chat is unique'),
React.createElement('p', {
key: 'subtitle',
className: "text-secondary max-w-2xl mx-auto"
}, 'The only messenger with military-grade cryptography')
]),
React.createElement('div', {
key: 'slider-container',
className: "relative max-w-4xl mx-auto"
}, [
React.createElement('div', {
key: 'slider-wrapper',
className: "overflow-hidden rounded-xl"
}, [
React.createElement('div', {
key: 'slides',
className: "flex transition-transform duration-500 ease-in-out",
style: { transform: `translateX(-${currentSlide * 100}%)` }
}, slides.map((slide, index) =>
React.createElement('div', {
key: index,
className: "w-full flex-shrink-0 px-4"
}, [
React.createElement('div', {
key: 'slide-content',
className: "card-minimal rounded-xl p-8 text-center min-h-[300px] flex flex-col justify-center relative overflow-hidden"
}, [
// Background icon
React.createElement('i', {
key: 'bg-icon',
className: `${slide.icon} absolute right-[-100px] top-1/2 -translate-y-1/2 opacity-10 text-[300px] pointer-events-none ${
slide.color === 'orange' ? 'text-orange-500' :
slide.color === 'yellow' ? 'text-yellow-500' :
slide.color === 'purple' ? 'text-purple-500' :
slide.color === 'green' ? 'text-green-500' :
slide.color === 'cyan' ? 'text-cyan-500' :
slide.color === 'blue' ? 'text-blue-500' :
'text-emerald-500'
}`
}),
// Content
React.createElement('h4', {
key: 'slide-title',
className: "text-xl font-semibold text-primary mb-4 relative z-10"
}, slide.title),
React.createElement('p', {
key: 'slide-description',
className: "text-secondary leading-relaxed max-w-2xl mx-auto relative z-10"
}, slide.description)
])
])
))
]),
// Navigation
React.createElement('button', {
key: 'prev-btn',
onClick: prevSlide,
className: "absolute left-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-gray-600/80 hover:bg-gray-500/80 text-white rounded-full flex items-center justify-center transition-all duration-200 z-10"
}, [
React.createElement('i', {
key: 'prev-icon',
className: "fas fa-chevron-left"
})
]),
React.createElement('button', {
key: 'next-btn',
onClick: nextSlide,
className: "absolute right-2 top-1/2 transform -translate-y-1/2 w-10 h-10 bg-gray-600/80 hover:bg-gray-500/80 text-white rounded-full flex items-center justify-center transition-all duration-200 z-10"
}, [
React.createElement('i', {
key: 'next-icon',
className: "fas fa-chevron-right"
})
])
]),
// Enhanced dots navigation (оставляем улучшенные точки)
React.createElement('div', {
key: 'dots-container',
className: "flex justify-center space-x-3 mt-6"
}, slides.map((slide, index) =>
React.createElement('button', {
key: index,
onClick: () => goToSlide(index),
className: `relative group transition-all duration-300 ${
index === currentSlide
? 'w-12 h-2 bg-orange-500 rounded-full'
: 'w-4 h-2 bg-gray-600 hover:bg-gray-500 rounded-full hover:scale-125'
}`
}, [
// Tooltip on hover
React.createElement('div', {
key: 'tooltip',
className: "absolute -top-10 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap pointer-events-none"
}, slide.title)
])
))
]);
};
// Enhanced Modern Slider Component with Original CSS Classes
const UniqueFeatureSlider = () => {
const trackRef = React.useRef(null);
const wrapRef = React.useRef(null);
const dotsRef = React.useRef(null);
const [current, setCurrent] = React.useState(0);
window.UniqueFeatureSlider = UniqueFeatureSlider;
const slides = [
{
icon: "🛡️",
bgImage: "linear-gradient(135deg, rgb(255 107 53 / 6%) 0%, rgb(255 140 66 / 45%) 100%)",
thumbIcon: "🔒",
title: "18-Layer Military Security",
description: "Revolutionary defense system with ECDH P-384 + AES-GCM 256 + ECDSA + Complete ASN.1 Validation."
},
{
icon: "🌐",
bgImage: "linear-gradient(135deg, rgb(147 51 234 / 6%) 0%, rgb(168 85 247 / 45%) 100%)",
thumbIcon: "🔗",
title: "Pure P2P WebRTC",
description: "Direct peer-to-peer connections without any servers. Complete decentralization with zero infrastructure."
},
{
icon: "🔄",
bgImage: "linear-gradient(135deg, rgb(16 185 129 / 6%) 0%, rgb(52 211 153 / 45%) 100%)",
thumbIcon: "⚡",
title: "Perfect Forward Secrecy",
description: "Automatic key rotation every 5 minutes. Non-extractable keys with hardware protection."
},
{
icon: "🎭",
bgImage: "linear-gradient(135deg, rgb(6 182 212 / 6%) 0%, rgb(34 211 238 / 45%) 100%)",
thumbIcon: "🌫️",
title: "Traffic Obfuscation",
description: "Fake traffic generation and pattern masking make communication indistinguishable from noise."
},
{
icon: "👁️",
bgImage: "linear-gradient(135deg, rgb(37 99 235 / 6%) 0%, rgb(59 130 246 / 45%) 100%)",
thumbIcon: "🚫",
title: "Zero Data Collection",
description: "No registration, no servers, no logs. Complete anonymity with instant channels."
}
];
const isMobile = () => window.matchMedia("(max-width:767px)").matches;
const center = React.useCallback((i) => {
if (!trackRef.current || !wrapRef.current) return;
const card = trackRef.current.children[i];
if (!card) return;
const axis = isMobile() ? "top" : "left";
const size = isMobile() ? "clientHeight" : "clientWidth";
const start = isMobile() ? card.offsetTop : card.offsetLeft;
wrapRef.current.scrollTo({
[axis]: start - (wrapRef.current[size] / 2 - card[size] / 2),
behavior: "smooth"
});
}, []);
const activate = React.useCallback((i, scroll = false) => {
if (i === current) return;
setCurrent(i);
if (scroll) {
setTimeout(() => center(i), 50);
}
}, [current, center]);
const go = (step) => {
const newIndex = Math.min(Math.max(current + step, 0), slides.length - 1);
activate(newIndex, true);
};
React.useEffect(() => {
const handleKeydown = (e) => {
if (["ArrowRight", "ArrowDown"].includes(e.key)) go(1);
if (["ArrowLeft", "ArrowUp"].includes(e.key)) go(-1);
};
window.addEventListener("keydown", handleKeydown, { passive: true });
return () => window.removeEventListener("keydown", handleKeydown);
}, [current]);
React.useEffect(() => {
center(current);
}, [current, center]);
return React.createElement('section', { style: { background: 'transparent' } }, [
// Header
React.createElement('div', {
key: 'head',
className: 'head'
}, [
React.createElement('h2', { key: 'title' }, 'Why SecureBit.chat is unique'),
React.createElement('div', {
key: 'controls',
className: 'controls'
}, [
React.createElement('button', {
key: 'prev',
id: 'prev-slider',
className: 'nav-btn',
'aria-label': 'Prev',
disabled: current === 0,
onClick: () => go(-1)
}, '‹'),
React.createElement('button', {
key: 'next',
id: 'next-slider',
className: 'nav-btn',
'aria-label': 'Next',
disabled: current === slides.length - 1,
onClick: () => go(1)
}, '›')
])
]),
// Slider
React.createElement('div', {
key: 'slider',
className: 'slider',
ref: wrapRef
},
React.createElement('div', {
className: 'track',
ref: trackRef
}, slides.map((slide, index) =>
React.createElement('article', {
key: index,
className: 'project-card',
...(index === current ? { active: '' } : {}),
onMouseEnter: () => {
if (window.matchMedia("(hover:hover)").matches) {
activate(index, true);
}
},
onClick: () => activate(index, true)
}, [
// Background
React.createElement('div', {
key: 'bg',
className: 'project-card__bg',
style: {
background: slide.bgImage,
backgroundSize: 'cover',
backgroundPosition: 'center'
}
}),
// Content
React.createElement('div', {
key: 'content',
className: 'project-card__content'
}, [
// Text container
React.createElement('div', { key: 'text' }, [
React.createElement('h3', {
key: 'title',
className: 'project-card__title'
}, slide.title),
React.createElement('p', {
key: 'desc',
className: 'project-card__desc'
}, slide.description)
])
])
])
))
),
// Dots
React.createElement('div', {
key: 'dots',
className: 'dots',
ref: dotsRef,
...(isMobile() ? { hidden: true } : {})
}, slides.map((_, index) =>
React.createElement('span', {
key: index,
className: `dot ${index === current ? 'active' : ''}`,
onClick: () => activate(index, true)
})
))
]);
};
// Export for use in your app
window.UniqueFeatureSlider = UniqueFeatureSlider;
+336
View File
@@ -167,3 +167,339 @@ tr.bg-black-table {
border-radius: 10px;
z-index: 1;
}
:root {
--gap: 1.25rem;
--speed: 0.55s cubic-bezier(0.25, 0.46, 0.45, 0.94);
--closed: 5rem;
--open: 30rem;
--accent: #fb923c;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Inter, sans-serif;
background: #07090d;
color: #c5c7ce;
}
.head {
max-width: 1400px;
margin: auto;
padding: 70px 20px 40px;
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 2rem;
}
.head h2 {
font: 400 1.5rem/1.2 Inter, sans-serif;
color: #fff;
}
@media (min-width: 1024px) {
.head h2 {
font-size: 2.25rem;
}
}
.nav-btn {
width: 2.5rem;
height: 2.5rem;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
color: #fff;
font-size: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: 0.3s;
}
.nav-btn:hover {
background: var(--accent);
}
.nav-btn:disabled {
opacity: 0.3;
cursor: default;
}
.slider {
max-width: 1400px;
margin: auto;
overflow: hidden;
}
.controls {
display: flex;
flex-direction: row;
gap: 0.5rem;
}
.track {
display: flex;
gap: var(--gap);
align-items: flex-start;
justify-content: center;
scroll-behavior: smooth;
scroll-snap-type: x mandatory;
padding-bottom: 40px;
}
.track::-webkit-scrollbar {
display: none;
}
.project-card {
position: relative;
flex: 0 0 var(--closed);
height: 26rem;
border-radius: 1rem;
overflow: hidden;
cursor: pointer;
transition: flex-basis var(--speed), transform var(--speed);
}
.project-card[active] {
flex-basis: var(--open);
transform: translateY(-6px);
box-shadow: 0 15px 12px rgba(0, 0, 0, 0.25);
}
.project-card__bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
filter: brightness(0.75) saturate(75%);
transition: filter 0.3s, transform var(--speed);
}
.project-card:hover .project-card__bg {
filter: brightness(0.9) saturate(100%);
transform: scale(1.06);
}
.project-card__content {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.7rem;
padding: 0;
background: linear-gradient(transparent 40%, rgba(0, 0, 0, 0.85) 100%);
z-index: 2;
}
.project-card__title {
color: #fff;
font-weight: 700;
font-size: 1.35rem;
writing-mode: vertical-rl;
transform: rotate(180deg);
}
.project-card__thumb,
.project-card__desc,
.project-card__btn {
display: none;
}
.project-card[active] .project-card__content {
flex-direction: row;
align-items: center;
padding: 1.2rem 2rem;
gap: 1.1rem;
}
.project-card[active] .project-card__title {
writing-mode: horizontal-tb;
transform: none;
font-size: 2.4rem;
}
.project-card[active] .project-card__thumb,
.project-card[active] .project-card__desc,
.project-card[active] .project-card__btn {
display: block;
}
.project-card__thumb {
width: 133px;
height: 269px;
border-radius: 0.45rem;
object-fit: cover;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);
}
.project-card__desc {
color: #ddd;
font-size: 1rem;
line-height: 1.4;
max-width: 16rem;
}
.project-card__btn {
padding: 0.55rem 1.3rem;
border: none;
border-radius: 9999px;
background: var(--accent);
color: #fff;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
}
.project-card__btn:hover {
background: #ff824f;
}
.dots {
display: flex;
gap: 0.5rem;
justify-content: center;
padding: 20px 0;
}
.dot {
width: 13px;
height: 13px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.35);
cursor: pointer;
transition: 0.3s;
}
.dot.active {
background: var(--accent);
transform: scale(1.2);
}
@media (max-width: 767px) {
:root {
--closed: 4rem;
--open: 22rem;
}
.head {
padding: 50px 20px 30px;
}
.track {
flex-direction: column;
scroll-snap-type: y mandatory;
align-items: center;
justify-content: flex-start;
padding-bottom: 0;
}
.project-card {
height: 20rem;
}
.project-card__title {
font-size: 1.1rem;
writing-mode: horizontal-tb;
transform: none;
text-align: center;
padding-inline: 0.3rem;
}
.nav-btn {
width: 2rem;
height: 2rem;
font-size: 1.2rem;
}
}
@media (max-width: 767px) {
:root {
--closed: 100%;
--open: 100%;
--gap: 0.8rem;
}
.head {
padding: 30px 15px 20px;
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.slider {
padding: 0 15px;
}
.track {
flex-direction: column;
scroll-snap-type: y mandatory;
gap: 0.8rem;
padding-bottom: 20px;
}
.project-card {
height: auto;
min-height: 80px;
flex: 0 0 auto;
width: 100%;
scroll-snap-align: start;
}
.project-card[active] {
min-height: 300px;
transform: none;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}
.project-card__content {
flex-direction: row;
justify-content: flex-start;
padding: 1rem;
align-items: center;
gap: 1rem;
}
.project-card__title {
writing-mode: horizontal-tb;
transform: none;
font-size: 1.2rem;
margin-right: auto;
}
.project-card__thumb,
.project-card__desc,
.project-card__btn {
display: none;
}
.project-card[active] .project-card__content {
align-items: flex-start;
padding: 1.5rem;
}
.project-card[active] .project-card__title {
font-size: 1.8rem;
margin-bottom: 1rem;
margin-top: 2rem;
}
.project-card[active] .project-card__thumb {
width: 200px;
height: 267px;
border-radius: 0.35rem;
margin-bottom: 1rem;
}
.project-card[active] .project-card__desc {
font-size: 0.95rem;
max-width: 100%;
margin-bottom: 1rem;
}
.project-card[active] .project-card__btn {
align-self: center;
width: 100%;
text-align: center;
padding: 0.7rem;
}
.dots {
display: none;
}
.controls {
width: 100%;
justify-content: space-between;
padding: 0 15px 20px;
}
.nav-btn {
position: static;
transform: none;
}
}