...
11
poc/components/faq.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<section class="faq-container">
|
||||||
|
<div class="markdown-content">
|
||||||
|
|
||||||
|
[[{{ config["name"] }}]]
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faq-section">
|
||||||
|
<h4>Frequently Asked Questions</h4>
|
||||||
|
[[{{ config["section_name"] }}]]
|
||||||
|
</div>
|
||||||
|
</section>
|
203
poc/components/footer.html
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
{% include 'components/login.html' %}
|
||||||
|
|
||||||
|
<footer class="tf_footer">
|
||||||
|
<div class="tf_footer_container">
|
||||||
|
<div class="tf_footer_section">
|
||||||
|
<h3>ThreeFold</h3>
|
||||||
|
<p>Building a decentralized internet <br>for a better world.</p>
|
||||||
|
</div>
|
||||||
|
<div class="tf_footer_section">
|
||||||
|
<h4>Links</h4>
|
||||||
|
<div class="footer_links">
|
||||||
|
<a href="#">About</a>
|
||||||
|
<a href="#">Technology</a>
|
||||||
|
<a href="#">Community</a>
|
||||||
|
<a href="#">Contact</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tf_footer_section">
|
||||||
|
<h4>Resources</h4>
|
||||||
|
<div class="footer_links">
|
||||||
|
<a href="#">Documentation</a>
|
||||||
|
<a href="#">Blog</a>
|
||||||
|
<a href="#">FAQ</a>
|
||||||
|
<a href="#">Support</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tf_footer_section">
|
||||||
|
<h4>Connect</h4>
|
||||||
|
<div class="footer_links">
|
||||||
|
<a href="#">Twitter</a>
|
||||||
|
<a href="#">Telegram</a>
|
||||||
|
<a href="#">GitHub</a>
|
||||||
|
<a href="#">Discord</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tf_footer_bottom">
|
||||||
|
<p>© 2024 ThreeFold. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
/* Light theme variables */
|
||||||
|
--footer-background-light: #FFFFFFF2;
|
||||||
|
--footer-text-light: #333;
|
||||||
|
--footer-link-light: #385bb5;
|
||||||
|
--footer-link-hover-light: #2a4494;
|
||||||
|
--footer-border-light: #eee;
|
||||||
|
|
||||||
|
/* Dark theme variables */
|
||||||
|
--footer-background-dark: #282C34F2;
|
||||||
|
--footer-text-dark: #fff;
|
||||||
|
--footer-link-dark: #7a9bff;
|
||||||
|
--footer-link-hover-dark: #a8beff;
|
||||||
|
--footer-border-dark: #444;
|
||||||
|
|
||||||
|
/* Default to dark theme */
|
||||||
|
--footer-background: var(--footer-background-dark);
|
||||||
|
--footer-text: var(--footer-text-dark);
|
||||||
|
--footer-link: var(--footer-link-dark);
|
||||||
|
--footer-link-hover: var(--footer-link-hover-dark);
|
||||||
|
--footer-border: var(--footer-border-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme class */
|
||||||
|
.light-theme {
|
||||||
|
--footer-background: var(--footer-background-light);
|
||||||
|
--footer-text: var(--footer-text-light);
|
||||||
|
--footer-link: var(--footer-link-light);
|
||||||
|
--footer-link-hover: var(--footer-link-hover-light);
|
||||||
|
--footer-border: var(--footer-border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer {
|
||||||
|
background-color: var(--footer-background);
|
||||||
|
color: var(--footer-text);
|
||||||
|
padding: 1.5rem 0 0.5rem;
|
||||||
|
margin-top: auto;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
max-height: 160px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 0rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_section h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin: 0 0 0.4rem;
|
||||||
|
color: var(--footer-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_section h4 {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin: 0 0 0.3rem;
|
||||||
|
color: var(--footer-text);
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_section p {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--footer-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer_links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer_links a {
|
||||||
|
color: var(--footer-link);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer_links a:hover {
|
||||||
|
color: var(--footer-link-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_bottom {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid var(--footer-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_bottom p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--footer-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.tf_footer_container {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer {
|
||||||
|
padding: 1rem 0 0.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.tf_footer_container {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer {
|
||||||
|
padding: 0.8rem 0 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_section {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_footer_section h4 {
|
||||||
|
padding-left: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer_links {
|
||||||
|
padding-left: 0.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Ensure theme changes are applied consistently across all components
|
||||||
|
function applyTheme(theme) {
|
||||||
|
document.documentElement.className = theme;
|
||||||
|
localStorage.setItem('theme', theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize theme from localStorage or default to dark
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const savedTheme = localStorage.getItem('theme') || '';
|
||||||
|
applyTheme(savedTheme);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Override the toggleTheme function from nav.html
|
||||||
|
function toggleTheme() {
|
||||||
|
const isLight = document.documentElement.classList.contains('light-theme');
|
||||||
|
applyTheme(isLight ? '' : 'light-theme');
|
||||||
|
}
|
||||||
|
</script>
|
62
poc/components/globe.html
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<div class="globe-container">
|
||||||
|
<canvas
|
||||||
|
id="cobe"
|
||||||
|
style="width: 500px; height: 500px"
|
||||||
|
width="1000"
|
||||||
|
height="1000"
|
||||||
|
></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.globe-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import createGlobe from 'https://cdn.skypack.dev/cobe'
|
||||||
|
|
||||||
|
let phi = 0
|
||||||
|
let canvas = document.getElementById("cobe")
|
||||||
|
|
||||||
|
// Detect system dark mode
|
||||||
|
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
|
||||||
|
const globe = createGlobe(canvas, {
|
||||||
|
devicePixelRatio: 2,
|
||||||
|
width: 1000,
|
||||||
|
height: 1000,
|
||||||
|
phi: 0,
|
||||||
|
theta: 0.3,
|
||||||
|
dark: isDarkMode ? 1 : 0,
|
||||||
|
diffuse: 1.2,
|
||||||
|
scale: 1,
|
||||||
|
mapSamples: 16000,
|
||||||
|
mapBrightness: isDarkMode ? 15 : 4,
|
||||||
|
baseColor: isDarkMode ? [0.8, 0.8, 0.8] : [0.3, 0.3, 0.3],
|
||||||
|
markerColor: [0.1, 0.8, 1],
|
||||||
|
glowColor: isDarkMode ? [0.6, 0.6, 0.6] : [0.2, 0.2, 0.2],
|
||||||
|
offset: [0, 0],
|
||||||
|
markers: [
|
||||||
|
{ location: [37.7595, -122.4367], size: 0.03 },
|
||||||
|
{ location: [40.7128, -74.006], size: 0.1 },
|
||||||
|
],
|
||||||
|
onRender: (state) => {
|
||||||
|
state.phi = phi
|
||||||
|
phi += 0.005
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for system theme changes
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||||||
|
const newIsDarkMode = e.matches
|
||||||
|
globe.dark = newIsDarkMode ? 1 : 0
|
||||||
|
globe.baseColor = newIsDarkMode ? [0.8, 0.8, 0.8] : [0.3, 0.3, 0.3]
|
||||||
|
globe.mapBrightness = newIsDarkMode ? 15 : 4
|
||||||
|
globe.glowColor = newIsDarkMode ? [0.6, 0.6, 0.6] : [0.2, 0.2, 0.2]
|
||||||
|
})
|
||||||
|
</script>
|
18
poc/components/header.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" x-data>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Decentralized Internet</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.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=Inter:wght@400;450;500;600&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="/static/ourworld.css">
|
||||||
|
<link rel="stylesheet" href="/static/menu.css">
|
||||||
|
<link rel="stylesheet" href="/static/login.css">
|
||||||
|
<link rel="stylesheet" href="/static/faq.css">
|
||||||
|
<!-- Load Alpine.js from CDN -->
|
||||||
|
<script defer src="https://unpkg.com/alpinejs@3.13.3/dist/cdn.min.js"></script>
|
||||||
|
</head>
|
133
poc/components/hero_image.html
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<div class="hero2_container" x-data="{ isZoomingIn: true }" x-init="setInterval(() => isZoomingIn = !isZoomingIn, 15000)">
|
||||||
|
<!-- Text Box -->
|
||||||
|
<div class="hero2_text-box">
|
||||||
|
<h2 class="hero2_title"> {{config["title"]}} </h2>
|
||||||
|
<p class="hero2_subtitle"> {{config["subtitle"]}} </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Image Wrapper -->
|
||||||
|
<div class="hero2_image-wrapper">
|
||||||
|
<img src="{{config["image"]}}"
|
||||||
|
class="hero2_floating-image"
|
||||||
|
:style="isZoomingIn ? 'transform: scale(1.5)' : 'transform: scale(1)'"
|
||||||
|
style="transition: transform 20s ease-in">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
main {
|
||||||
|
background-color: var(--body-background);
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
.hero2_container {
|
||||||
|
position: relative;
|
||||||
|
padding: 3rem;
|
||||||
|
background-color: var(--modal-background);
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
box-shadow: 0 8px 32px #00000026;
|
||||||
|
min-height: 400px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 2rem auto;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero2_text-box {
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
max-width: 500px;
|
||||||
|
padding-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero2_title {
|
||||||
|
font-size: clamp(1.5rem, 5vw, 2.5rem);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
text-shadow: 0 1px 2px #0000001A;
|
||||||
|
line-height: 1.2;
|
||||||
|
transition: color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero2_subtitle {
|
||||||
|
margin-top: 1rem;
|
||||||
|
background-color: #385bb5;
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
box-shadow: 0 2px 4px #0000001A;
|
||||||
|
font-size: clamp(0.875rem, 2vw, 1rem);
|
||||||
|
max-width: 100%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light-theme .hero2_subtitle {
|
||||||
|
background-color: #385bb5;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero2_image-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero2_floating-image {
|
||||||
|
width: 1000px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
transition: transform 15s ease-in;
|
||||||
|
margin-right: -100px;
|
||||||
|
transform-origin: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[x-cloak] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero2_container {
|
||||||
|
padding: 2rem;
|
||||||
|
min-height: 250px;
|
||||||
|
width: 90%;
|
||||||
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
.hero2_text-box {
|
||||||
|
padding-right: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.hero2_image-wrapper {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.hero2_floating-image {
|
||||||
|
margin-right: -200px;
|
||||||
|
}
|
||||||
|
.hero2_subtitle {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.hero2_container {
|
||||||
|
padding: 1.2rem;
|
||||||
|
min-height: 200px;
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
.hero2_floating-image {
|
||||||
|
margin-right: -200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
98
poc/components/hero_text.html
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<div class="hero1">
|
||||||
|
<div class="hero1_box">
|
||||||
|
<h1 class="hero1_title"></h1>
|
||||||
|
<p class="hero1_subtitle">[[{{ config["subtitle"] }}]]</p>
|
||||||
|
</div>
|
||||||
|
<div class="hero1_banner"> </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.hero1 {
|
||||||
|
position: relative;
|
||||||
|
padding: 2.5rem;
|
||||||
|
background-color: var(--hero-background);
|
||||||
|
border-radius: 1.2rem;
|
||||||
|
box-shadow: 0 8px 32px #0000004D;
|
||||||
|
min-height: 400px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2.5rem;
|
||||||
|
margin: 2rem auto;
|
||||||
|
color: var(--hero-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero1_box {
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
max-width: 500px;
|
||||||
|
flex-shrink: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero1_title {
|
||||||
|
font-size: clamp(1.5rem, 5vw, 2.5rem);
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--hero-text);
|
||||||
|
margin: 0;
|
||||||
|
text-shadow: 0 2px 4px #00000033;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero1_subtitle {
|
||||||
|
margin-top: 1rem;
|
||||||
|
background-color: var(--hero-subtitle-background);
|
||||||
|
color: var(--hero-subtitle-text);
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
border-radius: 0.4rem;
|
||||||
|
display: inline-block;
|
||||||
|
text-shadow: 0 1px 2px #00000033;
|
||||||
|
box-shadow: 0 2px 4px #00000033;
|
||||||
|
font-size: clamp(0.875rem, 2vw, 1rem);
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero1_banner {
|
||||||
|
flex-grow: 1;
|
||||||
|
background-color: var(--hero-banner-background);
|
||||||
|
padding: 1.3rem;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
font-size: 1rem;
|
||||||
|
min-height: 300px;
|
||||||
|
color: var(--hero-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero1 {
|
||||||
|
padding: 1.6rem;
|
||||||
|
min-height: 250px;
|
||||||
|
width: 90%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.hero1_box {
|
||||||
|
padding-right: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.hero1_subtitle {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
.hero1_banner {
|
||||||
|
padding: 1.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.hero1_banner {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.hero1 {
|
||||||
|
padding: 1rem;
|
||||||
|
min-height: 200px;
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
217
poc/components/identify_modal.html
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<div class="modal-overlay" id="identifyModal">
|
||||||
|
<div class="modal-container">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>Identify Yourself</h2>
|
||||||
|
<p class="modal-description">Enter your secret password to access the key management system.</p>
|
||||||
|
</div>
|
||||||
|
<form class="modal-form" id="identifyForm" onsubmit="return handleIdentify(event)">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="secret">Secret Password</label>
|
||||||
|
<input type="password" id="secret" name="secret" required
|
||||||
|
placeholder="Enter your secret password"
|
||||||
|
autocomplete="current-password">
|
||||||
|
<div class="error-message" id="errorMessage"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="closeIdentifyModal()">Cancel</button>
|
||||||
|
<button type="submit" class="btn btn-primary" id="identifyButton">Identify</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function showIdentifyModal() {
|
||||||
|
document.getElementById('identifyModal').style.display = 'block';
|
||||||
|
document.getElementById('secret').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeIdentifyModal() {
|
||||||
|
document.getElementById('identifyModal').style.display = 'none';
|
||||||
|
document.getElementById('identifyForm').reset();
|
||||||
|
document.getElementById('errorMessage').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleIdentify(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const secret = document.getElementById('secret').value;
|
||||||
|
const errorMessage = document.getElementById('errorMessage');
|
||||||
|
const identifyButton = document.getElementById('identifyButton');
|
||||||
|
|
||||||
|
// Disable button during processing
|
||||||
|
identifyButton.disabled = true;
|
||||||
|
identifyButton.textContent = 'Processing...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!window.kvs) {
|
||||||
|
throw new Error('Key management system not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the password in session storage
|
||||||
|
window.kvs.setPassword(secret);
|
||||||
|
|
||||||
|
// Try to generate/retrieve the private key to verify the password
|
||||||
|
await window.kvs.generate_private_key();
|
||||||
|
|
||||||
|
// If successful, close the modal
|
||||||
|
closeIdentifyModal();
|
||||||
|
|
||||||
|
// Dispatch an event to notify that identification was successful
|
||||||
|
window.dispatchEvent(new CustomEvent('userIdentified'));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Identification error:', error);
|
||||||
|
errorMessage.textContent = error.message || 'Invalid secret password';
|
||||||
|
errorMessage.style.display = 'block';
|
||||||
|
window.kvs.clearSession();
|
||||||
|
} finally {
|
||||||
|
// Re-enable button
|
||||||
|
identifyButton.disabled = false;
|
||||||
|
identifyButton.textContent = 'Identify';
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check session validity periodically
|
||||||
|
const sessionCheckInterval = setInterval(() => {
|
||||||
|
if (window.kvs && !window.kvs.isSessionValid()) {
|
||||||
|
window.kvs.clearSession();
|
||||||
|
showIdentifyModal();
|
||||||
|
}
|
||||||
|
}, 60000); // Check every minute
|
||||||
|
|
||||||
|
// Cleanup interval when page unloads
|
||||||
|
window.addEventListener('unload', () => {
|
||||||
|
clearInterval(sessionCheckInterval);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial check when the page loads
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
if (!window.kvs || !window.kvs.isSessionValid()) {
|
||||||
|
showIdentifyModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.modal-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1001;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-description {
|
||||||
|
color: #666;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
border-color: #007bff;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover:not(:disabled) {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #6c757d;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover:not(:disabled) {
|
||||||
|
background-color: #545b62;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: #dc3545;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: none;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: #fff5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ffebee;
|
||||||
|
}
|
||||||
|
</style>
|
123
poc/components/login.html
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<!-- Login Modal -->
|
||||||
|
<div id="loginModal" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-header">
|
||||||
|
<svg class="logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||||
|
</svg>
|
||||||
|
<h2>Welcome Back</h2>
|
||||||
|
<p>Sign in to your ThreeFold account</p>
|
||||||
|
</div>
|
||||||
|
<form id="login-form" onsubmit="return validateLoginForm(event)">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="login-email">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="login-email"
|
||||||
|
name="email"
|
||||||
|
required
|
||||||
|
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
autocomplete="email"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="login-emailError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="login-password">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="login-password"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="login-passwordError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-footer">
|
||||||
|
<label class="remember-me">
|
||||||
|
<input type="checkbox" name="remember"> Remember me
|
||||||
|
</label>
|
||||||
|
<a href="#" class="forgot-password">Forgot Password?</a>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="submit-button">Sign In</button>
|
||||||
|
</form>
|
||||||
|
<div class="signup-link">
|
||||||
|
Don't have an account? <a href="#" onclick="openSignupModal(); closeLoginModal();">Sign up</a>
|
||||||
|
</div>
|
||||||
|
<button class="close-button" onclick="closeLoginModal()">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function openLoginModal() {
|
||||||
|
const modal = document.getElementById('loginModal');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLoginModal() {
|
||||||
|
const modal = document.getElementById('loginModal');
|
||||||
|
modal.style.display = 'none';
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close modal when clicking outside
|
||||||
|
window.onclick = function(event) {
|
||||||
|
const modal = document.getElementById('loginModal');
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeLoginModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateLoginForm(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const email = document.getElementById('login-email');
|
||||||
|
const emailError = document.getElementById('login-emailError');
|
||||||
|
const password = document.getElementById('login-password');
|
||||||
|
const passwordError = document.getElementById('login-passwordError');
|
||||||
|
|
||||||
|
// Reset error messages
|
||||||
|
emailError.textContent = '';
|
||||||
|
passwordError.textContent = '';
|
||||||
|
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
// Email validation
|
||||||
|
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
if (!emailRegex.test(email.value)) {
|
||||||
|
emailError.textContent = 'Please enter a valid email address';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password validation
|
||||||
|
if (password.value.length < 8) {
|
||||||
|
passwordError.textContent = 'Password must be at least 8 characters long';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
// Here you would typically send the form data to your server
|
||||||
|
console.log('Form is valid, ready to submit');
|
||||||
|
closeLoginModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // Prevent form submission
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-time email validation
|
||||||
|
document.getElementById('login-email')?.addEventListener('input', function(e) {
|
||||||
|
const emailError = document.getElementById('login-emailError');
|
||||||
|
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
|
||||||
|
if (this.value && !emailRegex.test(this.value)) {
|
||||||
|
emailError.textContent = 'Please enter a valid email address';
|
||||||
|
} else {
|
||||||
|
emailError.textContent = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
60
poc/components/login_lightswitch.html
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<div class="tf_right_controls">
|
||||||
|
<button class="tf_signup_btn" onclick="openSignupModal()">Sign Up</button>
|
||||||
|
<button class="tf_login_btn" onclick="openLoginModal()">Login</button>
|
||||||
|
<button class="tf_theme_toggle" @click="document.documentElement.classList.toggle('light-theme')">
|
||||||
|
<svg class="theme-icon light" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="5"/>
|
||||||
|
<line x1="12" y1="1" x2="12" y2="3"/>
|
||||||
|
<line x1="12" y1="21" x2="12" y2="23"/>
|
||||||
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
||||||
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||||||
|
<line x1="1" y1="12" x2="3" y2="12"/>
|
||||||
|
<line x1="21" y1="12" x2="23" y2="12"/>
|
||||||
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
||||||
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||||
|
</svg>
|
||||||
|
<svg class="theme-icon dark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tf_right_controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2rem;
|
||||||
|
margin-left: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_signup_btn {
|
||||||
|
background: transparent;
|
||||||
|
border: 0.5px solid var(--text-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
padding: 0 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
height: 28px;
|
||||||
|
min-width: 80px;
|
||||||
|
line-height: 28px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_signup_btn:hover {
|
||||||
|
background: var(--text-color);
|
||||||
|
color: var(--body-background);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px #0000001A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_login_btn {
|
||||||
|
height: 28px;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
min-width: 80px;
|
||||||
|
line-height: 28px;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
25
poc/components/nav.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<div x-data="{ mobileMenuOpen: false }">
|
||||||
|
<nav class="tf_nav">
|
||||||
|
<div class="tf_nav_container">
|
||||||
|
<button class="hamburger-menu" @click="mobileMenuOpen = !mobileMenuOpen">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||||
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||||
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<a href="#" class="tf_logo">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||||
|
</svg>
|
||||||
|
<span>ThreeFold</span>
|
||||||
|
</a>
|
||||||
|
<ul class="tf_menu" :class="{ 'active': mobileMenuOpen }">
|
||||||
|
[[navcontent]]
|
||||||
|
</ul>
|
||||||
|
{% include 'components/login_lightswitch.html' %}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="mobile-overlay" :class="{ 'active': mobileMenuOpen }" @click="mobileMenuOpen = false"></div>
|
||||||
|
</div>
|
149
poc/components/roadmap.html
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<div class="roadmap-container">
|
||||||
|
<h1>Our Journey</h1>
|
||||||
|
<div class="timeline">
|
||||||
|
<div class="milestone">
|
||||||
|
<div class="milestone-date">2023 Q4</div>
|
||||||
|
<div class="milestone-content">
|
||||||
|
<h3>Platform Launch</h3>
|
||||||
|
<p>Initial release of our decentralized infrastructure and core services.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="milestone">
|
||||||
|
<div class="milestone-date">2024 Q1</div>
|
||||||
|
<div class="milestone-content">
|
||||||
|
<h3>Network Expansion</h3>
|
||||||
|
<p>Global node deployment and enhanced network capabilities.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="milestone">
|
||||||
|
<div class="milestone-date">2024 Q2</div>
|
||||||
|
<div class="milestone-content">
|
||||||
|
<h3>Developer Tools</h3>
|
||||||
|
<p>Release of comprehensive SDK and developer documentation.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="milestone">
|
||||||
|
<div class="milestone-date">2024 Q3</div>
|
||||||
|
<div class="milestone-content">
|
||||||
|
<h3>Enterprise Solutions</h3>
|
||||||
|
<p>Launch of enterprise-grade features and support services.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.roadmap-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 2rem 3rem;
|
||||||
|
background: var(--hero-background);
|
||||||
|
border-radius: 1.2rem;
|
||||||
|
box-shadow: 0 8px 32px #0000004D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roadmap-container h1 {
|
||||||
|
color: var(--hero-subtitle-background);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline {
|
||||||
|
position: relative;
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 85px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 2px;
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
position: relative;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 85px;
|
||||||
|
top: 15px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #888;
|
||||||
|
border: 2px solid var(--hero-background);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-date {
|
||||||
|
width: 70px;
|
||||||
|
padding-right: 1rem;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--hero-subtitle-background);
|
||||||
|
padding-top: 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-content {
|
||||||
|
flex: 1;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-left: 2rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-content h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: var(--hero-subtitle-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-content p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: var(--body-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.roadmap-container {
|
||||||
|
width: 90%;
|
||||||
|
padding: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline::before {
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone::before {
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-date {
|
||||||
|
width: 60px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.milestone-content {
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.roadmap-container {
|
||||||
|
width: 85%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
313
poc/components/signup.html
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
<!-- Signup Modal -->
|
||||||
|
<div id="signupModal" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-header">
|
||||||
|
<svg class="logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||||
|
</svg>
|
||||||
|
<h2>Join ThreeFold</h2>
|
||||||
|
<p>Create your ThreeFold account</p>
|
||||||
|
</div>
|
||||||
|
<form id="signup-form" onsubmit="return validateSignupForm(event)">
|
||||||
|
<div class="form-group compact">
|
||||||
|
<label for="signup-name">Full Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="signup-name"
|
||||||
|
name="name"
|
||||||
|
required
|
||||||
|
placeholder="Enter your full name"
|
||||||
|
autocomplete="name"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="signup-nameError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group compact">
|
||||||
|
<label for="signup-email">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="signup-email"
|
||||||
|
name="email"
|
||||||
|
required
|
||||||
|
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
autocomplete="email"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="signup-emailError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group compact">
|
||||||
|
<label for="signup-password">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="signup-password"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="signup-passwordError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group compact">
|
||||||
|
<label for="signup-tel">Phone Number</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
id="signup-tel"
|
||||||
|
name="tel"
|
||||||
|
required
|
||||||
|
placeholder="Enter your phone number"
|
||||||
|
autocomplete="tel"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="signup-telError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group compact">
|
||||||
|
<label for="signup-country">Country</label>
|
||||||
|
<select id="signup-country" name="country" required class="form-control">
|
||||||
|
<option value="">Select your country</option>
|
||||||
|
</select>
|
||||||
|
<span class="error-message" id="signup-countryError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group compact">
|
||||||
|
[[signup_interests]]
|
||||||
|
<span class="error-message" id="signup-interestsError"></span>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="submit-button">Sign Up</button>
|
||||||
|
</form>
|
||||||
|
<div class="signup-link">
|
||||||
|
Already have an account? <a href="#" onclick="openLoginModal(); closeSignupModal();">Sign in</a>
|
||||||
|
</div>
|
||||||
|
<button class="close-button" onclick="closeSignupModal()">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Populate countries dropdown
|
||||||
|
[[system/countries]]
|
||||||
|
|
||||||
|
const countrySelect = document.getElementById('signup-country');
|
||||||
|
countries.forEach(country => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = country;
|
||||||
|
option.textContent = country;
|
||||||
|
countrySelect.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
function openSignupModal() {
|
||||||
|
const modal = document.getElementById('signupModal');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSignupModal() {
|
||||||
|
const modal = document.getElementById('signupModal');
|
||||||
|
modal.style.display = 'none';
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close modal when clicking outside
|
||||||
|
window.onclick = function(event) {
|
||||||
|
const modal = document.getElementById('signupModal');
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeSignupModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-time validation functions
|
||||||
|
function validatePassword(password) {
|
||||||
|
return password.length >= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validatePhoneNumber(phone) {
|
||||||
|
return /^\+?[\d\s-]{10,}$/.test(phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateEmail(email) {
|
||||||
|
return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-time validation event listeners
|
||||||
|
document.getElementById('signup-password')?.addEventListener('input', function(e) {
|
||||||
|
const passwordError = document.getElementById('signup-passwordError');
|
||||||
|
if (this.value && !validatePassword(this.value)) {
|
||||||
|
passwordError.textContent = 'Password must be at least 8 characters long';
|
||||||
|
} else {
|
||||||
|
passwordError.textContent = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('signup-tel')?.addEventListener('input', function(e) {
|
||||||
|
const telError = document.getElementById('signup-telError');
|
||||||
|
if (this.value && !validatePhoneNumber(this.value)) {
|
||||||
|
telError.textContent = 'Please enter a valid phone number (min 10 digits)';
|
||||||
|
} else {
|
||||||
|
telError.textContent = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('signup-email')?.addEventListener('input', function(e) {
|
||||||
|
const emailError = document.getElementById('signup-emailError');
|
||||||
|
if (this.value && !validateEmail(this.value)) {
|
||||||
|
emailError.textContent = 'Please enter a valid email address';
|
||||||
|
} else {
|
||||||
|
emailError.textContent = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function validateSignupForm(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const name = document.getElementById('signup-name');
|
||||||
|
const email = document.getElementById('signup-email');
|
||||||
|
const password = document.getElementById('signup-password');
|
||||||
|
const tel = document.getElementById('signup-tel');
|
||||||
|
const country = document.getElementById('signup-country');
|
||||||
|
const interests = document.querySelectorAll('input[name="interests"]:checked');
|
||||||
|
|
||||||
|
const nameError = document.getElementById('signup-nameError');
|
||||||
|
const emailError = document.getElementById('signup-emailError');
|
||||||
|
const passwordError = document.getElementById('signup-passwordError');
|
||||||
|
const telError = document.getElementById('signup-telError');
|
||||||
|
const countryError = document.getElementById('signup-countryError');
|
||||||
|
const interestsError = document.getElementById('signup-interestsError');
|
||||||
|
|
||||||
|
// Reset error messages
|
||||||
|
nameError.textContent = '';
|
||||||
|
emailError.textContent = '';
|
||||||
|
passwordError.textContent = '';
|
||||||
|
telError.textContent = '';
|
||||||
|
countryError.textContent = '';
|
||||||
|
interestsError.textContent = '';
|
||||||
|
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
// Name validation
|
||||||
|
if (name.value.trim().length < 2) {
|
||||||
|
nameError.textContent = 'Please enter your full name';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email validation
|
||||||
|
if (!validateEmail(email.value)) {
|
||||||
|
emailError.textContent = 'Please enter a valid email address';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password validation
|
||||||
|
if (!validatePassword(password.value)) {
|
||||||
|
passwordError.textContent = 'Password must be at least 8 characters long';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phone validation
|
||||||
|
if (!validatePhoneNumber(tel.value)) {
|
||||||
|
telError.textContent = 'Please enter a valid phone number (min 10 digits)';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Country validation
|
||||||
|
if (!country.value) {
|
||||||
|
countryError.textContent = 'Please select your country';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interests validation
|
||||||
|
if (interests.length === 0) {
|
||||||
|
interestsError.textContent = 'Please select at least one interest';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
// Here you would typically send the form data to your server
|
||||||
|
console.log('Form is valid, ready to submit');
|
||||||
|
closeSignupModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Additional styles for signup form */
|
||||||
|
.form-group.compact {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group.compact label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group.compact input,
|
||||||
|
.form-group.compact select {
|
||||||
|
margin-bottom: 0;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.3rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--modal-text);
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label input[type="checkbox"] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.form-control {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--body-background);
|
||||||
|
color: var(--modal-text);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.3s;
|
||||||
|
height: 35px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.form-control:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #1a73e8;
|
||||||
|
box-shadow: 0 0 0 2px #1A73E833;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: #dc3545;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin: 0;
|
||||||
|
display: block;
|
||||||
|
min-height: 1rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
padding: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header h2 {
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
</style>
|
26
poc/content/navcontent.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
<li class="tf_menu_item">
|
||||||
|
<a href="../index.html" class="tf_menu_link" @click="mobileMenuOpen = false">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="tf_menu_item">
|
||||||
|
<a href="#" class="tf_menu_link" @click="mobileMenuOpen = false">Why</a>
|
||||||
|
<div class="tf_dropdown">
|
||||||
|
<a href="#" class="tf_dropdown_item">Sustainable</a>
|
||||||
|
<a href="#" class="tf_dropdown_item">Secure</a>
|
||||||
|
<a href="#" class="tf_dropdown_item">Scalable</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="tf_menu_item">
|
||||||
|
<a href="#" class="tf_menu_link" @click="mobileMenuOpen = false">Products</a>
|
||||||
|
<div class="tf_dropdown">
|
||||||
|
<a href="#" class="tf_dropdown_item">Fungistor</a>
|
||||||
|
<a href="#" class="tf_dropdown_item">Mycelium</a>
|
||||||
|
<a href="#" class="tf_dropdown_item">Magic Cloud</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="tf_menu_item">
|
||||||
|
<a href="#" class="tf_menu_link" @click="mobileMenuOpen = false">Info</a>
|
||||||
|
<div class="tf_dropdown">
|
||||||
|
<a href="/roadmap" class="tf_dropdown_item">Roadmap</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
35
poc/content/signup_interests.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<label><strong>Interests</strong> (check all which matters)</label>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="provider">
|
||||||
|
Become an cloud/internet provider (farmer)
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="vm">
|
||||||
|
Use cloud capacity for virtual machines
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="ai">
|
||||||
|
Use cloud capacity for AI
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="blockchain">
|
||||||
|
Use cloud capacity for Blockchain
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="data">
|
||||||
|
Use cloud capacity for Data
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="kubernetes">
|
||||||
|
Use cloud capacity for Kubernetes
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="partner">
|
||||||
|
Become a preferred Partner.
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="interests" value="development">
|
||||||
|
Develop applications (Web4).
|
||||||
|
</label>
|
||||||
|
</div>
|
24
poc/content/system/countries.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const countries = [
|
||||||
|
"Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Antigua and Barbuda", "Argentina", "Armenia", "Australia",
|
||||||
|
"Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin",
|
||||||
|
"Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Brazil", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
|
||||||
|
"Cambodia", "Cameroon", "Canada", "Cape Verde", "Central African Republic", "Chad", "Chile", "China", "Colombia",
|
||||||
|
"Comoros", "Congo", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica",
|
||||||
|
"Dominican Republic", "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia",
|
||||||
|
"Ethiopia", "Fiji", "Finland", "France", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Greece", "Grenada",
|
||||||
|
"Guatemala", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Honduras", "Hungary", "Iceland", "India", "Indonesia",
|
||||||
|
"Iran", "Iraq", "Ireland", "Israel", "Italy", "Ivory Coast", "Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya",
|
||||||
|
"Kiribati", "North Korea", "South Korea", "Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon", "Lesotho", "Liberia",
|
||||||
|
"Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Maldives",
|
||||||
|
"Mali", "Malta", "Marshall Islands", "Mauritania", "Mauritius", "Mexico", "Micronesia", "Moldova", "Monaco",
|
||||||
|
"Mongolia", "Montenegro", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands",
|
||||||
|
"New Zealand", "Nicaragua", "Niger", "Nigeria", "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea",
|
||||||
|
"Paraguay", "Peru", "Philippines", "Poland", "Portugal", "Qatar", "Romania", "Russia", "Rwanda", "Saint Kitts and Nevis",
|
||||||
|
"Saint Lucia", "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia",
|
||||||
|
"Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia",
|
||||||
|
"South Africa", "South Sudan", "Spain", "Sri Lanka", "Sudan", "Suriname", "Swaziland", "Sweden", "Switzerland",
|
||||||
|
"Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Togo", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
|
||||||
|
"Turkmenistan", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States", "Uruguay",
|
||||||
|
"Uzbekistan", "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Yemen", "Zambia", "Zimbabwe"
|
||||||
|
];
|
||||||
|
|
23
poc/content/threefold/faq_tf.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
## About Our Platform
|
||||||
|
|
||||||
|
### What We Offer
|
||||||
|
|
||||||
|
ThreeFold provides a revolutionary Internet Infrastructure that enables:
|
||||||
|
|
||||||
|
- Decentralized cloud, data & network capacity with reward opportunities for contributors
|
||||||
|
- Global deployment of web2, web3, AI, edge, and blockchain applications
|
||||||
|
- Enhanced application access through distributed infrastructure
|
||||||
|
- Creation of resilient, uncensorable peer-to-peer networks
|
||||||
|
|
||||||
|
### Why ThreeFold?
|
||||||
|
|
||||||
|
- **First platform in the world to seamlessly combine cloud, data & network capabilities**
|
||||||
|
- Global Access: Bridging the digital divide for 50% of the world lacking quality Internet infrastructure
|
||||||
|
- Economic Sovereignty: Enabling countries to build and control their own Internet infrastructure
|
||||||
|
- Sustainable Growth: Reducing environmental impact through efficient edge computing
|
||||||
|
|
||||||
|
### Our Mission & Values
|
||||||
|
|
||||||
|
- Planet First: Committed to sustainable and eco-friendly technology solutions
|
||||||
|
- People Empowerment: Democratizing Internet infrastructure access and ownership
|
||||||
|
- Digital Sovereignty: Ensuring data privacy and infrastructure independence
|
92
poc/content/threefold/faq_tf_intro.html
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<details>
|
||||||
|
<summary>What is web4?</summary>
|
||||||
|
<p>
|
||||||
|
No, ThreeFold is a complementary Internet and lives with and on top of
|
||||||
|
the current Internet. From out of ThreeFold you can still use the
|
||||||
|
current Internet and interact with it.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Why do we need a new Internet?</summary>
|
||||||
|
<p>
|
||||||
|
The Internet used to be a peer to peer network, but has become fragile
|
||||||
|
and too centralized. There are so many problems with the current
|
||||||
|
Internet, such as authenticity, privacy, security, and sustainability
|
||||||
|
that we believe a fundamental new approach is needed.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>This sounds too big, are you guys are faking it?</summary>
|
||||||
|
<p>
|
||||||
|
We have been working on this for over 30 years and ThreeFold is the
|
||||||
|
result of that work. We have a working product and a growing community
|
||||||
|
of farmers, users, and partners. We are real and we are here to stay.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>You have 2 tokens, TFT and INCA, why?</summary>
|
||||||
|
<p>
|
||||||
|
Thanks to our community there are +60,000 virtual cpu's and +17,000 GB
|
||||||
|
of storage available on the network.
|
||||||
|
<a
|
||||||
|
href="https://dashboard.grid.tf/#/tf-grid/node-statistics/"
|
||||||
|
target="_blank"
|
||||||
|
>Checkout our stats on our dashboard.</a
|
||||||
|
>
|
||||||
|
TFT is our token which was used to build generation 1,2 and 3 of the
|
||||||
|
ThreeFold Grid of capacity. TFT is the reward for our loyal community.
|
||||||
|
There can never be more than 1 billion TFT. We are now building
|
||||||
|
generation 4 of the ThreeFold Grid of capacity and we need a new token
|
||||||
|
to build this new generation. There will never be more than 3 billion
|
||||||
|
INCA. Our partners will start selling new ThreeFold Nodes end Nov 2024
|
||||||
|
with a new reward scheme and ready to grow to millions of nodes.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>How can I participate?</summary>
|
||||||
|
<p>
|
||||||
|
You can participate by becoming a farmer, a user, a partner or by developing a web4 app.
|
||||||
|
<ul>
|
||||||
|
<li><a href="/farmer">Provide capacity to the ThreeFold Grid</a></li>
|
||||||
|
<li><a href="/user">Use capacity off the ThreeFold Grid</a></li>
|
||||||
|
<li><a href="/builder">Build solutions</a></li>
|
||||||
|
<li><a href="/hero_coder">Develop applications for Web4</a></li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>What is Web4?</summary>
|
||||||
|
<p>
|
||||||
|
Web4 is the next generation of the Internet, where users are in 100% in
|
||||||
|
control of their data. No centralized services are needed. Blockchain
|
||||||
|
was the first step to Web3, ThreeFold is the next step to Web4.
|
||||||
|
ThreeFold is ofcourse fully compatible with Web2 and Web3. In Web4 each
|
||||||
|
user has a personal Virtual Digital Assistent which is called a Hero.
|
||||||
|
This hero manages your digital life and on your behalf can interact with
|
||||||
|
all versions of the Web.
|
||||||
|
<a href="/hero_intro">Checkout more info about our Hero</a>.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>How secure and private is my data?</summary>
|
||||||
|
<p>
|
||||||
|
ThreeFold is designed to be secure and private by default. We use
|
||||||
|
end-to-end encryption to protect your data and ensure that only you have
|
||||||
|
access to your data.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Who should use the ThreeFold Grid. ?</summary>
|
||||||
|
<p>
|
||||||
|
Individuals, businesses, and organizations who want to be sovereign and have full control over their data and applications.
|
||||||
|
Security is a very big problem today, Technology as used by ThreeFold has the potential to resolve this if used properly.
|
||||||
|
We already work with Governements, NGO's, and Individuals. We are building a channel of solution providers and integrators who want to build on top of ThreeFold.
|
||||||
|
</p>
|
||||||
|
</details>
|
94
poc/content/threefold/faq_tf_section.html
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<details>
|
||||||
|
<summary>Is this a separate new Internet?</summary>
|
||||||
|
<p>
|
||||||
|
No, ThreeFold is a complementary Internet and lives with and on top of
|
||||||
|
the current Internet. From out of ThreeFold you can still use the
|
||||||
|
current Internet and interact with it.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Why do we need a new Internet?</summary>
|
||||||
|
<p>
|
||||||
|
The Internet used to be a peer to peer network, but has become fragile
|
||||||
|
and too centralized. There are so many problems with the current
|
||||||
|
Internet, such as authenticity, privacy, security, and sustainability
|
||||||
|
that we believe a fundamental new approach is needed.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>This sounds too big, are you guys are faking it?</summary>
|
||||||
|
<p>
|
||||||
|
We have been working on this for over 30 years and ThreeFold is the
|
||||||
|
result of that work. We have a working product and a growing community
|
||||||
|
of farmers, users, and partners. We are real and we are here to stay.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>You have 2 tokens, TFT and INCA, why?</summary>
|
||||||
|
<p>
|
||||||
|
Thanks to our community there are +60,000 virtual cpu's and +17,000 GB
|
||||||
|
of storage available on the network.
|
||||||
|
<a
|
||||||
|
href="https://dashboard.grid.tf/#/tf-grid/node-statistics/"
|
||||||
|
target="_blank"
|
||||||
|
>Checkout our stats on our dashboard.</a
|
||||||
|
>
|
||||||
|
TFT is our token which was used to build generation 1,2 and 3 of the
|
||||||
|
ThreeFold Grid of capacity. TFT is the reward for our loyal community.
|
||||||
|
There can never be more than 1 billion TFT. We are now building
|
||||||
|
generation 4 of the ThreeFold Grid of capacity and we need a new token
|
||||||
|
to build this new generation. There will never be more than 3 billion
|
||||||
|
INCA. Our partners will start selling new ThreeFold Nodes end Nov 2024
|
||||||
|
with a new reward scheme and ready to grow to millions of nodes.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>How can I participate?</summary>
|
||||||
|
<p>
|
||||||
|
You can participate by becoming a farmer, a user, a partner or by developing a web4 app.
|
||||||
|
<ul>
|
||||||
|
<li><a href="/farmer">Provide capacity to the ThreeFold Grid</a></li>
|
||||||
|
<li><a href="/user">Use capacity off the ThreeFold Grid</a></li>
|
||||||
|
<li><a href="/builder">Build solutions</a></li>
|
||||||
|
<li><a href="/hero_coder">Develop applications for Web4</a></li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>What is Web4?</summary>
|
||||||
|
<p>
|
||||||
|
Web4 is the next generation of the Internet, where users are in control of our data 100% and there is 0 centralization.
|
||||||
|
Blockchain was the first step to Web3, ThreeFold is the next step to Web4.
|
||||||
|
ThreeFold is ofcourse fully compatible with Web2 and Web3.
|
||||||
|
In Web4 each user has a personal Virtual Digital Assistent which is called a Hero.
|
||||||
|
This hero manages your digital life and on your behalf can interact with all versions of the Web.
|
||||||
|
<a href="/hero_intro">Checkout more info about our Hero</a>.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>How secure and private is my data?</summary>
|
||||||
|
<p>
|
||||||
|
ThreeFold is designed to be secure and private by default. We use
|
||||||
|
end-to-end encryption to protect your data and ensure that only you have
|
||||||
|
access to your data.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Do you offer custom plans for businesses?</summary>
|
||||||
|
<p>
|
||||||
|
Yes, we have a network of Integrators who can help you design a custom
|
||||||
|
plan.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>What payment methods do you accept?</summary>
|
||||||
|
<p>We accept credit cards (v4) and our own tokens TFT and INCA.</p>
|
||||||
|
</details>
|
43
poc/content/threefold/faq_tf_web4.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<details>
|
||||||
|
<summary>What is Web4?</summary>
|
||||||
|
<p>
|
||||||
|
Web4 is the next generation of the Internet, where users are in 100% in
|
||||||
|
control of their data. No centralized services are needed. Blockchain
|
||||||
|
was the first step to Web3, ThreeFold is the next step to Web4.
|
||||||
|
ThreeFold is ofcourse fully compatible with Web2 and Web3. In Web4 each
|
||||||
|
user has a personal Virtual Digital Assistent which is called a Hero.
|
||||||
|
This hero manages your digital life and on your behalf can interact with
|
||||||
|
all versions of the Web.
|
||||||
|
<a href="/hero_intro">Checkout more info about our Hero</a>.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>How secure and private is my data?</summary>
|
||||||
|
<p>
|
||||||
|
ThreeFold is designed to be secure and private by default. We use
|
||||||
|
end-to-end encryption to protect your data and ensure that only you have
|
||||||
|
access to your data.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>How secure and private is my data?</summary>
|
||||||
|
<p>
|
||||||
|
ThreeFold is designed to be secure and private by default. We use
|
||||||
|
end-to-end encryption to protect your data and ensure that only you have
|
||||||
|
access to your data.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Who should care bout Web4. ?</summary>
|
||||||
|
<p>
|
||||||
|
While blockchain is a very good first step, we believe its time to move
|
||||||
|
to the next level. Web4 is the next generation of the Internet, where
|
||||||
|
users are in 100% in control of their data. This hero manages your
|
||||||
|
digital life and on your behalf can interact with all versions of the
|
||||||
|
Web. Enterprises, Governments, Software Developers and Individuals
|
||||||
|
should care about Web4.
|
||||||
|
</p>
|
||||||
|
</details>
|
4
poc/content/threefold/hero_test.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
**The need for a decentralized internet has never been more crucial in our increasingly connected world.**
|
||||||
|
|
||||||
|
|
||||||
|
Our current internet infrastructure is dominated by a handful of tech giants who control vast amounts of our personal data, creating significant privacy concerns and single points of failure. This centralization not only compromises our digital sovereignty but also makes the entire system vulnerable to censorship, surveillance, and cyber attacks.
|
23
poc/index.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% include 'components/header.html' %}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% include 'components/nav.html' %}
|
||||||
|
<main>
|
||||||
|
{% include 'components/globe.html' %}
|
||||||
|
|
||||||
|
{% set config = {
|
||||||
|
"title": "THREEFOLD AUTONOMOUS INTERNET.",
|
||||||
|
"subtitle": "Building a decentralized internet, for a better world..",
|
||||||
|
"image": "static/cloud_dancing.png" }
|
||||||
|
%}
|
||||||
|
{% include 'components/hero_image.html' %}
|
||||||
|
|
||||||
|
{% set config = { "name": "threefold/faq_tf", "section_name": "threefold/faq_tf_section" } %}
|
||||||
|
{% include 'components/faq.html' %}
|
||||||
|
|
||||||
|
</main>
|
||||||
|
{% include 'components/footer.html' %}
|
||||||
|
{% include 'components/login.html' %}
|
||||||
|
{% include 'components/signup.html' %}
|
||||||
|
</body>
|
||||||
|
</html>
|
283
poc/login2222.html
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login - ThreeFold</title>
|
||||||
|
<link rel="stylesheet" href="components/nav.html">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-header">
|
||||||
|
<svg class="logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||||
|
</svg>
|
||||||
|
<h1>Welcome Back</h1>
|
||||||
|
<p>Sign in to your ThreeFold account</p>
|
||||||
|
</div>
|
||||||
|
<form id="loginForm" onsubmit="return validateForm(event)">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
required
|
||||||
|
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
autocomplete="email"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="emailError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
minlength="8"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
>
|
||||||
|
<span class="error-message" id="passwordError"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-footer">
|
||||||
|
<label class="remember-me">
|
||||||
|
<input type="checkbox" name="remember"> Remember me
|
||||||
|
</label>
|
||||||
|
<a href="#" class="forgot-password">Forgot Password?</a>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="login-button">Sign In</button>
|
||||||
|
</form>
|
||||||
|
<div class="signup-link">
|
||||||
|
Don't have an account? <a href="#">Sign up</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-color: #1a73e8;
|
||||||
|
--error-color: #dc3545;
|
||||||
|
--text-color: #333;
|
||||||
|
--border-color: #ddd;
|
||||||
|
--background-color: #f5f5f5;
|
||||||
|
--box-shadow: 0 2px 10px #0000001A;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
background: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-box {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header p {
|
||||||
|
margin: 0.5rem 0 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 0.5rem; /* Reduced from 1rem */
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="email"],
|
||||||
|
input[type="password"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: border-color 0.3s, box-shadow 0.3s;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="email"]:focus,
|
||||||
|
input[type="password"]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 2px #1A73E833;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
display: block;
|
||||||
|
color: var(--error-color);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
min-height: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem; /* Reduced from 1rem */
|
||||||
|
}
|
||||||
|
|
||||||
|
.remember-me {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s, transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:hover {
|
||||||
|
background: #1557b0;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-link {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-link a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-link a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.login-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-box {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function validateForm(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const email = document.getElementById('email');
|
||||||
|
const emailError = document.getElementById('emailError');
|
||||||
|
const password = document.getElementById('password');
|
||||||
|
const passwordError = document.getElementById('passwordError');
|
||||||
|
|
||||||
|
// Reset error messages
|
||||||
|
emailError.textContent = '';
|
||||||
|
passwordError.textContent = '';
|
||||||
|
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
// Email validation
|
||||||
|
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
if (!emailRegex.test(email.value)) {
|
||||||
|
emailError.textContent = 'Please enter a valid email address';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password validation
|
||||||
|
if (password.value.length < 8) {
|
||||||
|
passwordError.textContent = 'Password must be at least 8 characters long';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
// Here you would typically send the form data to your server
|
||||||
|
console.log('Form is valid, ready to submit');
|
||||||
|
// For demo purposes, we'll just log the email
|
||||||
|
console.log('Email:', email.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-time email validation
|
||||||
|
document.getElementById('email').addEventListener('input', function(e) {
|
||||||
|
const emailError = document.getElementById('emailError');
|
||||||
|
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
|
||||||
|
if (this.value && !emailRegex.test(this.value)) {
|
||||||
|
emailError.textContent = 'Please enter a valid email address';
|
||||||
|
} else {
|
||||||
|
emailError.textContent = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
299
poc/logintest.html
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
{% include 'components/header.html' %}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% include 'components/nav.html' %}
|
||||||
|
<main>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Identity Test Page</h1>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div id="status" class="status unauthenticated">
|
||||||
|
Not identified
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button class="btn-identify" onclick="showIdentifyModal()">Identify</button>
|
||||||
|
<button class="btn-clear" onclick="clearAllAndReload()">Clear All & Reload</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="keyInfo" class="key-info" style="display: none;">
|
||||||
|
<h3>Key Information</h3>
|
||||||
|
<div class="key-details">
|
||||||
|
<div id="keyStatus"></div>
|
||||||
|
<div id="publicKey" class="public-key"></div>
|
||||||
|
<div id="generateKeySection" style="display: none;" class="generate-key-section">
|
||||||
|
<div class="warning-message">
|
||||||
|
⚠️ No private key found. You need to generate a private key to use the system.
|
||||||
|
<strong>WARNING: Losing your private key will result in permanent loss of access to your digital life in ThreeFold.</strong>
|
||||||
|
</div>
|
||||||
|
<button onclick="generatePrivateKey()" class="btn-generate">Generate Private Key</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="testActions" class="test-actions" style="display: none;">
|
||||||
|
<h3>Test Actions</h3>
|
||||||
|
<div class="test-container">
|
||||||
|
<div class="test-group">
|
||||||
|
<input type="text" id="testKey" placeholder="Key name">
|
||||||
|
<input type="text" id="testValue" placeholder="Value">
|
||||||
|
<button onclick="setTestValue()">Set Value</button>
|
||||||
|
<div id="setResult" class="set-result"></div>
|
||||||
|
</div>
|
||||||
|
<div class="test-group">
|
||||||
|
<input type="text" id="retrieveKey" placeholder="Key to retrieve">
|
||||||
|
<button onclick="getTestValue()">Get Value</button>
|
||||||
|
<div id="retrievedValue" class="retrieved-value"></div>
|
||||||
|
</div>
|
||||||
|
<div class="test-group">
|
||||||
|
<button onclick="listAllValues()" class="btn-list">List All Values</button>
|
||||||
|
<div id="allValues" class="all-values"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Identity Modal -->
|
||||||
|
<div class="modal-overlay" id="identifyModal">
|
||||||
|
<div class="modal-container">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>Identify Yourself</h2>
|
||||||
|
<p class="modal-description">Enter your secret password to access the key management system.</p>
|
||||||
|
</div>
|
||||||
|
<form class="modal-form" id="identifyForm" onsubmit="return handleIdentify(event)">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="secret">Secret Password</label>
|
||||||
|
<input type="password" id="secret" name="secret" required
|
||||||
|
placeholder="Enter your secret password"
|
||||||
|
autocomplete="current-password">
|
||||||
|
<div class="error-message" id="errorMessage"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="closeIdentifyModal()">Cancel</button>
|
||||||
|
<button type="submit" class="btn btn-primary" id="identifyButton">Identify</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function showIdentifyModal() {
|
||||||
|
document.getElementById('identifyModal').style.display = 'block';
|
||||||
|
document.getElementById('secret').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeIdentifyModal() {
|
||||||
|
document.getElementById('identifyModal').style.display = 'none';
|
||||||
|
document.getElementById('identifyForm').reset();
|
||||||
|
document.getElementById('errorMessage').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllAndReload() {
|
||||||
|
sessionStorage.clear();
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generatePrivateKey() {
|
||||||
|
try {
|
||||||
|
const privateKey = await window.kvs.generateNewPrivateKey();
|
||||||
|
const publicKey = window.kvs.getPublicKey(privateKey);
|
||||||
|
const publicKeyBase58 = bs58.encode(publicKey);
|
||||||
|
|
||||||
|
document.getElementById('generateKeySection').style.display = 'none';
|
||||||
|
document.getElementById('keyStatus').textContent = '🔑 New private key generated';
|
||||||
|
document.getElementById('publicKey').innerHTML =
|
||||||
|
`<strong>Public Key (Base58):</strong><br>${publicKeyBase58}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating private key:', error);
|
||||||
|
document.getElementById('keyStatus').textContent =
|
||||||
|
'❌ Error generating private key: ' + error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateStatus() {
|
||||||
|
const status = document.getElementById('status');
|
||||||
|
const keyInfo = document.getElementById('keyInfo');
|
||||||
|
const testActions = document.getElementById('testActions');
|
||||||
|
const keyStatus = document.getElementById('keyStatus');
|
||||||
|
const generateKeySection = document.getElementById('generateKeySection');
|
||||||
|
const publicKeyDiv = document.getElementById('publicKey');
|
||||||
|
|
||||||
|
if (window.kvs.isSessionValid()) {
|
||||||
|
try {
|
||||||
|
status.className = 'status authenticated';
|
||||||
|
status.textContent = 'Identified successfully';
|
||||||
|
|
||||||
|
keyInfo.style.display = 'block';
|
||||||
|
testActions.style.display = 'block';
|
||||||
|
|
||||||
|
const hasKey = await window.kvs.hasPrivateKey();
|
||||||
|
if (!hasKey) {
|
||||||
|
generateKeySection.style.display = 'block';
|
||||||
|
keyStatus.textContent = '⚠️ No private key found';
|
||||||
|
publicKeyDiv.innerHTML = '';
|
||||||
|
testActions.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
generateKeySection.style.display = 'none';
|
||||||
|
const privateKey = await window.kvs.getPrivateKey();
|
||||||
|
const publicKey = window.kvs.getPublicKey(privateKey);
|
||||||
|
const publicKeyBase58 = bs58.encode(publicKey);
|
||||||
|
|
||||||
|
keyStatus.textContent = '🔑 Private key loaded';
|
||||||
|
publicKeyDiv.innerHTML = `<strong>Public Key (Base58):</strong><br>${publicKeyBase58}`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
status.className = 'status unauthenticated';
|
||||||
|
status.textContent = 'Error accessing keys: ' + error.message;
|
||||||
|
keyInfo.style.display = 'none';
|
||||||
|
testActions.style.display = 'none';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status.className = 'status unauthenticated';
|
||||||
|
status.textContent = 'Not identified';
|
||||||
|
keyInfo.style.display = 'none';
|
||||||
|
testActions.style.display = 'none';
|
||||||
|
showIdentifyModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleIdentify(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const secret = document.getElementById('secret').value;
|
||||||
|
const errorMessage = document.getElementById('errorMessage');
|
||||||
|
const identifyButton = document.getElementById('identifyButton');
|
||||||
|
|
||||||
|
// Disable button during processing
|
||||||
|
identifyButton.disabled = true;
|
||||||
|
identifyButton.textContent = 'Processing...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!window.kvs) {
|
||||||
|
throw new Error('Key management system not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the password in session storage
|
||||||
|
window.kvs.setPassword(secret);
|
||||||
|
|
||||||
|
// If successful, close the modal
|
||||||
|
closeIdentifyModal();
|
||||||
|
|
||||||
|
// Dispatch an event to notify that identification was successful
|
||||||
|
window.dispatchEvent(new CustomEvent('userIdentified'));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Identification error:', error);
|
||||||
|
errorMessage.textContent = error.message || 'Invalid secret password';
|
||||||
|
errorMessage.style.display = 'block';
|
||||||
|
window.kvs.clearSession();
|
||||||
|
} finally {
|
||||||
|
// Re-enable button
|
||||||
|
identifyButton.disabled = false;
|
||||||
|
identifyButton.textContent = 'Identify';
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setTestValue() {
|
||||||
|
const key = document.getElementById('testKey').value;
|
||||||
|
const value = document.getElementById('testValue').value;
|
||||||
|
const setResult = document.getElementById('setResult');
|
||||||
|
const retrieveKey = document.getElementById('retrieveKey');
|
||||||
|
|
||||||
|
if (!key || !value) {
|
||||||
|
setResult.innerHTML = '<div class="error">Please enter both key and value</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await window.kvs.set(key, value);
|
||||||
|
setResult.innerHTML = '<div class="success">Value set successfully!</div>';
|
||||||
|
document.getElementById('testKey').value = '';
|
||||||
|
document.getElementById('testValue').value = '';
|
||||||
|
|
||||||
|
// Auto-fill the retrieve key field
|
||||||
|
retrieveKey.value = key;
|
||||||
|
// Automatically get the value
|
||||||
|
await getTestValue();
|
||||||
|
// Refresh the list if it's visible
|
||||||
|
const allValues = document.getElementById('allValues');
|
||||||
|
if (allValues.innerHTML !== '') {
|
||||||
|
await listAllValues();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setResult.innerHTML = `<div class="error">Error setting value: ${error.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTestValue() {
|
||||||
|
const key = document.getElementById('retrieveKey').value;
|
||||||
|
const retrievedValueDiv = document.getElementById('retrievedValue');
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
retrievedValueDiv.innerHTML = '<div class="error">Please enter a key to retrieve</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const value = await window.kvs.get(key);
|
||||||
|
retrievedValueDiv.innerHTML = value ?
|
||||||
|
`<div class="success">Retrieved value: ${value}</div>` :
|
||||||
|
'<div class="info">No value found for this key</div>';
|
||||||
|
} catch (error) {
|
||||||
|
retrievedValueDiv.innerHTML = `<div class="error">Error: ${error.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listAllValues() {
|
||||||
|
const allValuesDiv = document.getElementById('allValues');
|
||||||
|
try {
|
||||||
|
const results = await window.kvs.listAll();
|
||||||
|
if (results.length === 0) {
|
||||||
|
allValuesDiv.innerHTML = '<div class="no-values">No stored values found</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = results.map(({key, value, encrypted}) => `
|
||||||
|
<div class="stored-value ${encrypted ? 'encrypted' : 'not-encrypted'}">
|
||||||
|
<div class="stored-key">${key}</div>
|
||||||
|
<div class="stored-value-content">${value}</div>
|
||||||
|
<div class="encryption-status">
|
||||||
|
${encrypted ?
|
||||||
|
'<span class="status-icon">🔒</span> Encrypted' :
|
||||||
|
'<span class="status-icon">⚠️</span> Not encrypted'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
allValuesDiv.innerHTML = html;
|
||||||
|
} catch (error) {
|
||||||
|
allValuesDiv.innerHTML = `<div class="error">Error listing values: ${error.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check session validity periodically
|
||||||
|
const sessionCheckInterval = setInterval(() => {
|
||||||
|
if (window.kvs && !window.kvs.isSessionValid()) {
|
||||||
|
window.kvs.clearSession();
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
}, 60000); // Check every minute
|
||||||
|
|
||||||
|
// Cleanup interval when page unloads
|
||||||
|
window.addEventListener('unload', () => {
|
||||||
|
clearInterval(sessionCheckInterval);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial check when the page loads
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
updateStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update status when user is identified
|
||||||
|
window.addEventListener('userIdentified', updateStatus);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
9
poc/page1.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{% include 'components/header.html' %}
|
||||||
|
<body>
|
||||||
|
{% include 'components/nav.html' %}
|
||||||
|
<main>
|
||||||
|
{% include 'components/hero1.html' %}
|
||||||
|
</main>
|
||||||
|
{% include 'components/footer.html' %}
|
||||||
|
</body>
|
||||||
|
</html>
|
10
poc/page2.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
{% include 'components/header.html' %}
|
||||||
|
<body>
|
||||||
|
{% include 'components/nav.html' %}
|
||||||
|
<main>
|
||||||
|
{% include 'components/faq.html' %}
|
||||||
|
</main>
|
||||||
|
{% include 'components/footer.html' %}
|
||||||
|
</body>
|
||||||
|
</html>
|
1
poc/remember.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{/* <script src="https://cdn.jsdelivr.net/npm/tweetnacl@1.0.3/nacl.min.js"></script> */}}
|
21
poc/roadmap.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% include 'components/header.html' %}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% include 'components/nav.html' %}
|
||||||
|
<main>
|
||||||
|
|
||||||
|
{% set config = {
|
||||||
|
"title": "THREEFOLD INTERNET ROADMAP",
|
||||||
|
"subtitle": "Building a decentralized internet, for a better world",
|
||||||
|
"image": "static/questions.png" }
|
||||||
|
%}
|
||||||
|
{% include 'components/hero_image.html' %}
|
||||||
|
|
||||||
|
{% set config_roadmap = { "name": "threefold/faq_tf", "section_name": "threefold/faq_tf_section" } %}
|
||||||
|
{% include 'components/roadmap.html' %}
|
||||||
|
|
||||||
|
</main>
|
||||||
|
{% include 'components/footer.html' %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
93
poc/server.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from fastapi import FastAPI, Request, HTTPException
|
||||||
|
from fastapi.responses import HTMLResponse, FileResponse
|
||||||
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from jinja2 import Environment, FileSystemLoader, select_autoescape, TemplateNotFound
|
||||||
|
import os
|
||||||
|
import markdown
|
||||||
|
import re
|
||||||
|
|
||||||
|
sources_dir = "/Users/despiegk1/code/git.ourworld.tf/freeflowuniverse/heroweb/research/dancing_banner/"
|
||||||
|
static_dir = f"{sources_dir}/static"
|
||||||
|
content_dir = f"{sources_dir}/content"
|
||||||
|
|
||||||
|
def get_content(name: str) -> str:
|
||||||
|
"""Get content by name from either HTML or markdown files in content directory"""
|
||||||
|
# Remove any leading/trailing slashes
|
||||||
|
name = name.strip('/')
|
||||||
|
|
||||||
|
# Check for file with .html extension
|
||||||
|
html_path = os.path.join(content_dir, f"{name}.html")
|
||||||
|
if os.path.exists(html_path):
|
||||||
|
with open(html_path, 'r') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
# Check for file with .md extension
|
||||||
|
md_path = os.path.join(content_dir, f"{name}.md")
|
||||||
|
if os.path.exists(md_path):
|
||||||
|
with open(md_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
return markdown.markdown(content)
|
||||||
|
|
||||||
|
return f"[[{name} not found]]"
|
||||||
|
|
||||||
|
def process_content(content: str) -> str:
|
||||||
|
"""Process content and replace [[name]] with corresponding HTML content"""
|
||||||
|
def replace_content(match):
|
||||||
|
name = match.group(1)
|
||||||
|
return get_content(name)
|
||||||
|
|
||||||
|
# Replace all [[name]] patterns
|
||||||
|
return re.sub(r'\[\[(.*?)\]\]', replace_content, content)
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
if not os.path.exists(static_dir):
|
||||||
|
raise RuntimeError(f"The directory '{static_dir}' does not exist.")
|
||||||
|
|
||||||
|
if not os.path.exists(sources_dir):
|
||||||
|
raise RuntimeError(f"The templates dir '{sources_dir}' does not exist.")
|
||||||
|
|
||||||
|
# Mount the static files directory
|
||||||
|
app.mount("/static", StaticFiles(directory=static_dir), name="static")
|
||||||
|
|
||||||
|
env = Environment(
|
||||||
|
loader=FileSystemLoader(sources_dir),
|
||||||
|
autoescape=select_autoescape(['html', 'xml'])
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize Jinja2 templates
|
||||||
|
templates = Jinja2Templates(directory=sources_dir)
|
||||||
|
|
||||||
|
@app.get("/favicon.ico")
|
||||||
|
async def favicon():
|
||||||
|
# First try to serve from static directory
|
||||||
|
favicon_path = os.path.join(static_dir, "favicon.ico")
|
||||||
|
if os.path.exists(favicon_path):
|
||||||
|
return FileResponse(favicon_path)
|
||||||
|
# If not found, return 404
|
||||||
|
raise HTTPException(status_code=404, detail="Favicon not found")
|
||||||
|
|
||||||
|
@app.get("/", response_class=HTMLResponse)
|
||||||
|
async def read_index(request: Request):
|
||||||
|
template = env.get_template("index.html")
|
||||||
|
content = template.render(request=request)
|
||||||
|
return process_content(content)
|
||||||
|
|
||||||
|
@app.get("/{path:path}", response_class=HTMLResponse)
|
||||||
|
async def read_template(request: Request, path: str):
|
||||||
|
# Add .html extension if not present
|
||||||
|
if not path.endswith('.html'):
|
||||||
|
path = f"{path}.html"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to load and render the template (this will work for both direct files and templates)
|
||||||
|
template = env.get_template(path)
|
||||||
|
content = template.render(request=request)
|
||||||
|
return process_content(content)
|
||||||
|
except TemplateNotFound:
|
||||||
|
raise HTTPException(status_code=404, detail=f"Template {path} not found")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run("server:app", host="127.0.0.1", port=8001, reload=True)
|
BIN
poc/static/cloud_dancing.png
Normal file
After Width: | Height: | Size: 602 KiB |
BIN
poc/static/cloud_dancing2.png
Normal file
After Width: | Height: | Size: 1.3 MiB |
154
poc/static/faq.css
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/* Import Google Fonts - if not already imported in ourworld.css */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap');
|
||||||
|
|
||||||
|
/* Container styling */
|
||||||
|
.faq-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Markdown content styling */
|
||||||
|
.markdown-content {
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 2rem;
|
||||||
|
border-right: 1px solid #a4b6ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base styles for markdown content */
|
||||||
|
.markdown-content {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Heading styles */
|
||||||
|
.markdown-content h2,
|
||||||
|
.markdown-content h3 {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme colors */
|
||||||
|
.light-theme .markdown-content h2,
|
||||||
|
.light-theme .markdown-content h3,
|
||||||
|
.light-theme .markdown-content p,
|
||||||
|
.light-theme .markdown-content ul,
|
||||||
|
.light-theme .markdown-content li {
|
||||||
|
color: #444444 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme colors */
|
||||||
|
.markdown-content h2,
|
||||||
|
.markdown-content h3,
|
||||||
|
.markdown-content p,
|
||||||
|
.markdown-content ul,
|
||||||
|
.markdown-content li {
|
||||||
|
color: #cccccc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 200;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content h3 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 200;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Regular text styles */
|
||||||
|
.markdown-content p,
|
||||||
|
.markdown-content ul,
|
||||||
|
.markdown-content li {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make list items and paragraphs consistent size */
|
||||||
|
.markdown-content p,
|
||||||
|
.markdown-content li {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List specific styling */
|
||||||
|
.markdown-content ul {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content li {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FAQ section styling */
|
||||||
|
.faq-section {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding: 1em 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary::marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
details[open] summary {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
details p {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary:before {
|
||||||
|
content: "▶";
|
||||||
|
display: inline-block;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
details[open] summary:before {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.faq-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content {
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
}
|
||||||
|
}
|
5
poc/static/favicon.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
||||||
|
<rect width="24" height="24" fill="#1a1a1a"/>
|
||||||
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 345 B |
BIN
poc/static/happy_kid.png
Normal file
After Width: | Height: | Size: 293 KiB |
178
poc/static/login.css
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/* Modal Styles */
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #00000080;
|
||||||
|
z-index: 1001;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: var(--modal-background);
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px #00000033;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 460px;
|
||||||
|
color: var(--modal-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-box {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--modal-text);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header p {
|
||||||
|
margin: 0.5rem 0 0;
|
||||||
|
color: var(--hover-color);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
color: var(--modal-text);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--body-background);
|
||||||
|
color: var(--modal-text);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.3s;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #1a73e8;
|
||||||
|
box-shadow: 0 0 0 2px #1A73E833;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: #dc3545;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
display: block;
|
||||||
|
min-height: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remember-me {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
color: var(--modal-text);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password {
|
||||||
|
color: #1a73e8;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: #1a73e8;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button:hover {
|
||||||
|
background: #1557b0;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-link {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
color: var(--modal-text);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-link a {
|
||||||
|
color: #1a73e8;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-link a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: -1rem;
|
||||||
|
right: -1rem;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: none;
|
||||||
|
background: var(--modal-text);
|
||||||
|
color: var(--modal-background);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.modal-content {
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
367
poc/static/login_test.css
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2rem;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 1rem 0;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.authenticated {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.unauthenticated {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-identify, .btn-clear, .btn-list, .btn-generate {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-identify {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-identify:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-clear:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-list {
|
||||||
|
background-color: #17a2b8;
|
||||||
|
color: white;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-list:hover {
|
||||||
|
background-color: #138496;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-generate {
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
margin-top: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-generate:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-info {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-details {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-message {
|
||||||
|
background-color: #fff3cd;
|
||||||
|
color: #856404;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 1rem 0;
|
||||||
|
border: 1px solid #ffeeba;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-key {
|
||||||
|
word-break: break-all;
|
||||||
|
font-family: monospace;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-actions {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-group input {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #2c3e50;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-group button {
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-group button:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set-result, .retrieved-value {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background-color: #e2e3e5;
|
||||||
|
color: #383d41;
|
||||||
|
border: 1px solid #d6d8db;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-values {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stored-value {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stored-key {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stored-value-content {
|
||||||
|
font-family: monospace;
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.encryption-status {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.encrypted .encryption-status {
|
||||||
|
color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-encrypted .encryption-status {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-values {
|
||||||
|
text-align: center;
|
||||||
|
color: #6c757d;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal Styles */
|
||||||
|
.modal-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: white;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1001;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-description {
|
||||||
|
color: #666;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
border-color: #007bff;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover:not(:disabled) {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #6c757d;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover:not(:disabled) {
|
||||||
|
background-color: #545b62;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: #dc3545;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: none;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: #fff5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ffebee;
|
||||||
|
}
|
||||||
|
</style>
|
272
poc/static/menu.css
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
.tf_nav {
|
||||||
|
background: var(--body-background);
|
||||||
|
padding: 0.3rem;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 1px 4px #00000022;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_nav_container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-right: 4rem;
|
||||||
|
height: 100%;
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_logo svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu_item {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu_link {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.3rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
transition: color 0.2s;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 450;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu_link:hover {
|
||||||
|
color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
background: var(--body-background);
|
||||||
|
min-width: 150px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 4px 12px #00000033;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu_item:hover .tf_dropdown {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_dropdown_item {
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_dropdown_item:hover {
|
||||||
|
background-color: #80808019;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_right_controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2rem;
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_theme_toggle {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid var(--text-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.3rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_theme_toggle:hover {
|
||||||
|
background-color: #80808019;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light-theme .theme-icon.light {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light-theme .theme-icon.dark {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-icon.light {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-icon.dark {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_login_btn {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--text-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
padding: 0.25rem 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_login_btn:hover {
|
||||||
|
background: var(--text-color);
|
||||||
|
color: var(--body-background);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px #0000001A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger-menu {
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
padding: 0.5rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger-menu svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 999;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overlay.active {
|
||||||
|
display: block;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.tf_menu {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 48px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--body-background);
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 1rem;
|
||||||
|
height: auto;
|
||||||
|
box-shadow: 0 4px 12px #00000033;
|
||||||
|
z-index: 1000;
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
overflow-y: auto;
|
||||||
|
transform: translateY(-100%);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu.active {
|
||||||
|
display: flex;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu_item {
|
||||||
|
height: auto;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_menu_link {
|
||||||
|
padding: 0.8rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_dropdown {
|
||||||
|
position: static;
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_dropdown_item {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_logo {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger-menu {
|
||||||
|
display: block;
|
||||||
|
order: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tf_right_controls {
|
||||||
|
margin-left: auto;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
}
|
BIN
poc/static/music.png
Normal file
After Width: | Height: | Size: 702 KiB |
187
poc/static/ourworld.css
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/* Import Google Fonts */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Light theme variables */
|
||||||
|
--body-background-light: #ffffff;
|
||||||
|
--body-text-light: #333333;
|
||||||
|
--text-color-light: #333;
|
||||||
|
--hover-color-light: #666;
|
||||||
|
--modal-background-light: #f5f5f5;
|
||||||
|
--modal-text-light: #333;
|
||||||
|
--input-border-light: #ddd;
|
||||||
|
--hero-background-light: #FFFFFFF2;
|
||||||
|
--hero-background2-light: #FFFFFFFB;
|
||||||
|
--hero-text-light: #333;
|
||||||
|
--hero-banner-background-light: #0000000D;
|
||||||
|
--hero-subtitle-background-light: #385bb5;
|
||||||
|
--hero-subtitle-text-light: white;
|
||||||
|
--input-border-light: var(--hero-background2-light);
|
||||||
|
|
||||||
|
/* Dark theme variables */
|
||||||
|
--body-background-dark: #1a1a1a;
|
||||||
|
--body-text-dark: #ffffff;
|
||||||
|
--text-color-dark: #fff;
|
||||||
|
--hover-color-dark: #aaa;
|
||||||
|
--modal-background-dark: #282c34;
|
||||||
|
--modal-text-dark: #fff;
|
||||||
|
--input-border-dark: #444;
|
||||||
|
--hero-background-dark: #282C34F2;
|
||||||
|
--hero-background2-dark: #2D3139FA;
|
||||||
|
--hero-text-dark: white;
|
||||||
|
--hero-banner-background-dark: #FFFFFF26;
|
||||||
|
--hero-subtitle-background-dark: #385bb5;
|
||||||
|
--hero-subtitle-text-dark: white;
|
||||||
|
--input-border-dark: var(--hero-background2-dark);
|
||||||
|
|
||||||
|
/* Default to dark theme */
|
||||||
|
--body-background: var(--body-background-dark);
|
||||||
|
--body-text: var(--body-text-dark);
|
||||||
|
--text-color: var(--text-color-dark);
|
||||||
|
--hover-color: var(--hover-color-dark);
|
||||||
|
--modal-background: var(--modal-background-dark);
|
||||||
|
--modal-text: var(--modal-text-dark);
|
||||||
|
--input-border: var(--input-border-dark);
|
||||||
|
--hero-background: var(--hero-background-dark);
|
||||||
|
--hero-background2: var(--hero-background2-dark);
|
||||||
|
--hero-text: var(--hero-text-dark);
|
||||||
|
--hero-banner-background: var(--hero-banner-background-dark);
|
||||||
|
--hero-subtitle-background: var(--hero-subtitle-background-dark);
|
||||||
|
--hero-subtitle-text: var(--hero-subtitle-text-dark);
|
||||||
|
--input-border: var(--input-border-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme class */
|
||||||
|
.light-theme {
|
||||||
|
--body-background: var(--body-background-light);
|
||||||
|
--body-text: var(--body-text-light);
|
||||||
|
--text-color: var(--text-color-light);
|
||||||
|
--hover-color: var(--hover-color-light);
|
||||||
|
--modal-background: var(--modal-background-light);
|
||||||
|
--modal-text: var(--modal-text-light);
|
||||||
|
--input-border: var(--input-border-light);
|
||||||
|
--hero-background: var(--hero-background-light);
|
||||||
|
--hero-background2: var(--hero-background2-light);
|
||||||
|
--hero-text: var(--hero-text-light);
|
||||||
|
--hero-banner-background: var(--hero-banner-background-light);
|
||||||
|
--hero-subtitle-background: var(--hero-subtitle-background-light);
|
||||||
|
--hero-subtitle-text: var(--hero-subtitle-text-light);
|
||||||
|
--input-border: var(--input-border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Heading styles - using Poppins */
|
||||||
|
h1 {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
line-height: 1.1;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: -0.005em;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
p ul {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
margin: 1.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p li {
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paragraph styles - using Roboto */
|
||||||
|
p {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
max-width: 720px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optional: styling for small or additional text */
|
||||||
|
small {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Additional styles for links within text */
|
||||||
|
a {
|
||||||
|
color: #007acc;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation styles - using Poppins */
|
||||||
|
nav {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover {
|
||||||
|
color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background-color: var(--body-background);
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
background-color: var(--body-background);
|
||||||
|
color: var(--body-text);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.25rem 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
BIN
poc/static/questions.png
Normal file
After Width: | Height: | Size: 387 KiB |
BIN
poc/static/questions_.png
Normal file
After Width: | Height: | Size: 1.2 MiB |