first commit
BIN
.vs/BiohazardWeb/v16/.suo
Normal file
9
.vs/VSWorkspaceState.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ExpandedNodes": [
|
||||||
|
"",
|
||||||
|
"\\css",
|
||||||
|
"\\js"
|
||||||
|
],
|
||||||
|
"SelectedNode": "\\index.html",
|
||||||
|
"PreviewInSolutionExplorer": false
|
||||||
|
}
|
||||||
BIN
.vs/slnx.sqlite
Normal file
214
admin.html
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Admin — Biohazard VFX</title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="css/styles.css">
|
||||||
|
<style>
|
||||||
|
#admin.content-block {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 12vh var(--gutter-x) 8vh;
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
color: var(--acid);
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
border-bottom: 1px solid var(--graphite);
|
||||||
|
background: none;
|
||||||
|
min-height: 60vh;
|
||||||
|
}
|
||||||
|
.admin-title {
|
||||||
|
font-family: "ArchiveCond", sans-serif;
|
||||||
|
font-size: 2rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--hot);
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
}
|
||||||
|
.admin-section {
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
background: var(--graphite);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.18);
|
||||||
|
padding: 2rem 1.5rem 1.2rem;
|
||||||
|
color: var(--acid);
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
max-width: 600px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.admin-section h2 {
|
||||||
|
font-family: "ArchiveCond", sans-serif;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--hot);
|
||||||
|
margin-bottom: 0.7rem;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.admin-section p {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--acid);
|
||||||
|
opacity: 0.88;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
}
|
||||||
|
.admin-warning {
|
||||||
|
color: #ffb6b6;
|
||||||
|
background: #3d1a1a;
|
||||||
|
border: 1px solid #c66;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 1em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
.admin-cms {
|
||||||
|
background: var(--graphite);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 18px rgba(0,0,0,0.28);
|
||||||
|
padding: 2.5rem 2rem 2rem;
|
||||||
|
max-width: 400px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 2rem auto;
|
||||||
|
border: 1px solid #222;
|
||||||
|
}
|
||||||
|
.admin-cms h2 {
|
||||||
|
color: var(--acid);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-family: 'ArchiveCond', sans-serif;
|
||||||
|
}
|
||||||
|
.admin-cms label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--acid);
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.admin-cms input,
|
||||||
|
.admin-cms select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.7em;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
border: 1px solid var(--graphite);
|
||||||
|
border-radius: 5px;
|
||||||
|
background: var(--dark);
|
||||||
|
color: var(--acid);
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.admin-cms button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.7em;
|
||||||
|
background: #00ffe7;
|
||||||
|
color: #181818;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 8px #00ffe733;
|
||||||
|
transition: background 0.2s, color 0.2s;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
.admin-cms .feedback {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-top: 1em;
|
||||||
|
min-height: 2em;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="nav-placeholder"></div>
|
||||||
|
<main id="admin" class="content-block">
|
||||||
|
<form class="admin-cms" id="projectForm" autocomplete="off">
|
||||||
|
<h2>Add New Project</h2>
|
||||||
|
<label for="title">Title*</label>
|
||||||
|
<input type="text" id="title" name="title" required maxlength="80" autocomplete="off">
|
||||||
|
|
||||||
|
<label for="size">Size</label>
|
||||||
|
<select id="size" name="size">
|
||||||
|
<option value="small" selected>Small</option>
|
||||||
|
<option value="big">Big</option>
|
||||||
|
<option value="wide">Wide</option>
|
||||||
|
<option value="tall">Tall</option>
|
||||||
|
<option value="banner">Banner</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="embed">Embed URL*</label>
|
||||||
|
<input type="url" id="embed" name="embed" required placeholder="https://player.vimeo.com/..." autocomplete="off">
|
||||||
|
|
||||||
|
<label for="thumb">Thumbnail*</label>
|
||||||
|
<input type="file" id="thumb" name="thumb" accept=".jpg,.jpeg,.png,.webp,.gif" required>
|
||||||
|
|
||||||
|
<label for="info">Project Info</label>
|
||||||
|
<textarea id="info" name="info" rows="3" style="width:100%;padding:0.7em;margin-top:0.3rem;border:1px solid var(--graphite);border-radius:5px;background:var(--dark);color:var(--acid);font-family:'SpaceMono',monospace;font-size:1rem;"></textarea>
|
||||||
|
|
||||||
|
<label for="credits">Credits</label>
|
||||||
|
<textarea id="credits" name="credits" rows="3" style="width:100%;padding:0.7em;margin-top:0.3rem;border:1px solid var(--graphite);border-radius:5px;background:var(--dark);color:var(--acid);font-family:'SpaceMono',monospace;font-size:1rem;"></textarea>
|
||||||
|
|
||||||
|
<button type="submit">Add Project</button>
|
||||||
|
<div class="feedback" id="feedback"></div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<div id="socials-placeholder"></div>
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Biohazard VFX. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
// Inject nav fragment
|
||||||
|
fetch('fragments/nav.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('nav-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Inject socials fragment
|
||||||
|
fetch('fragments/socials.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('socials-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Micro-CMS form logic
|
||||||
|
const API_URL = '/api/projects';
|
||||||
|
const API_KEY = 'YOUR_API_SECRET_HERE'; // <-- Set this to your API_SECRET
|
||||||
|
const form = document.getElementById('projectForm');
|
||||||
|
const feedback = document.getElementById('feedback');
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
feedback.textContent = '';
|
||||||
|
feedback.className = 'feedback';
|
||||||
|
const fd = new FormData(form);
|
||||||
|
try {
|
||||||
|
const res = await fetch(API_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'x-api-key': API_KEY },
|
||||||
|
body: fd
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.ok) {
|
||||||
|
feedback.textContent = '✅ Project added!';
|
||||||
|
feedback.classList.add('ok');
|
||||||
|
form.reset();
|
||||||
|
} else {
|
||||||
|
feedback.textContent = '❌ ' + (data.error || 'Error');
|
||||||
|
feedback.classList.add('err');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
feedback.textContent = '❌ Network error';
|
||||||
|
feedback.classList.add('err');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
132
admin/api.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* Biohazard VFX Micro-CMS API
|
||||||
|
*
|
||||||
|
* Installation:
|
||||||
|
* npm i express multer dotenv
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* PORT=4000 node admin/api.js
|
||||||
|
* # or use PM2 for process management
|
||||||
|
*
|
||||||
|
* Reverse Proxy:
|
||||||
|
* Proxy /api/* to http://localhost:4000 via Nginx Proxy Manager.
|
||||||
|
*
|
||||||
|
* Security:
|
||||||
|
* Protect /admin/* and /api/* with basic-auth or Cloudflare Zero-Trust.
|
||||||
|
*
|
||||||
|
* SMB Share:
|
||||||
|
* Ensure new files are world-readable (0644) for Nginx.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const multer = require('multer');
|
||||||
|
const fs = require('fs');
|
||||||
|
const fsp = require('fs/promises');
|
||||||
|
const path = require('path');
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const API_SECRET = process.env.API_SECRET;
|
||||||
|
const PORT = process.env.PORT || 4000;
|
||||||
|
const PROJECTS_JSON = path.resolve(__dirname, '../projects/projects.json');
|
||||||
|
const PROJECTS_DIR = path.resolve(__dirname, '../projects');
|
||||||
|
const TMP_DIR = path.resolve(__dirname, 'tmp');
|
||||||
|
|
||||||
|
if (!API_SECRET) {
|
||||||
|
console.error('Missing API_SECRET in environment.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(TMP_DIR)) fs.mkdirSync(TMP_DIR, { recursive: true });
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
const upload = multer({ dest: TMP_DIR });
|
||||||
|
|
||||||
|
function slugify(str) {
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]+/g, '_')
|
||||||
|
.replace(/^_+|_+$/g, '')
|
||||||
|
.slice(0, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeExt(filename) {
|
||||||
|
const ext = path.extname(filename).toLowerCase();
|
||||||
|
return ext.match(/\.(jpg|jpeg|png|webp|gif)$/) ? ext : '.jpg';
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireApiKey(req, res, next) {
|
||||||
|
if (req.headers['x-api-key'] !== API_SECRET) {
|
||||||
|
return res.status(401).json({ ok: false, error: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get('/api/projects', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const data = await fsp.readFile(PROJECTS_JSON, 'utf8');
|
||||||
|
res.type('json').send(data);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).json({ ok: false, error: 'Cannot read manifest.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/projects', requireApiKey, upload.single('thumb'), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { title, size = 'small', embed, credits = '', info = '' } = req.body;
|
||||||
|
const file = req.file;
|
||||||
|
if (!title || !embed || !file) {
|
||||||
|
if (file) fs.unlinkSync(file.path);
|
||||||
|
return res.status(400).json({ ok: false, error: 'Missing required fields.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load manifest
|
||||||
|
let manifest = [];
|
||||||
|
try {
|
||||||
|
manifest = JSON.parse(await fsp.readFile(PROJECTS_JSON, 'utf8'));
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const index = String(manifest.length + 1).padStart(2, '0');
|
||||||
|
const slug = slugify(title);
|
||||||
|
const folderName = `${index}_${slug}`;
|
||||||
|
const folderPath = path.join(PROJECTS_DIR, folderName);
|
||||||
|
|
||||||
|
await fsp.mkdir(folderPath, { recursive: true });
|
||||||
|
|
||||||
|
// Move thumbnail
|
||||||
|
const ext = safeExt(file.originalname);
|
||||||
|
const thumbName = `thumbnail${ext}`;
|
||||||
|
const thumbDest = path.join(folderPath, thumbName);
|
||||||
|
await fsp.rename(file.path, thumbDest);
|
||||||
|
await fsp.chmod(thumbDest, 0o644);
|
||||||
|
|
||||||
|
// Write info.txt and credits.txt with provided content
|
||||||
|
await fsp.writeFile(path.join(folderPath, 'info.txt'), info, { mode: 0o644 });
|
||||||
|
await fsp.writeFile(path.join(folderPath, 'credits.txt'), credits, { mode: 0o644 });
|
||||||
|
|
||||||
|
// Build new project object
|
||||||
|
const newObj = {
|
||||||
|
id: folderName,
|
||||||
|
title,
|
||||||
|
thumbnail: `projects/${folderName}/${thumbName}`,
|
||||||
|
size,
|
||||||
|
embed,
|
||||||
|
credits: `projects/${folderName}/credits.txt`,
|
||||||
|
info: `projects/${folderName}/info.txt`
|
||||||
|
};
|
||||||
|
|
||||||
|
manifest.push(newObj);
|
||||||
|
await fsp.writeFile(PROJECTS_JSON, JSON.stringify(manifest, null, 2), { mode: 0o644 });
|
||||||
|
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
if (req.file && fs.existsSync(req.file.path)) fs.unlinkSync(req.file.path);
|
||||||
|
res.status(500).json({ ok: false, error: 'Server error.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Biohazard VFX micro-CMS running on port ${PORT}`);
|
||||||
|
});
|
||||||
138
contact.html
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<!DOCTYPE html><html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Connect</title>
|
||||||
|
<link rel="stylesheet" href="css/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- =======================================================
|
||||||
|
NAV
|
||||||
|
======================================================= -->
|
||||||
|
<header class="nav">
|
||||||
|
<a href="index.html" class="logo">BIOHAZARD VFX</a>
|
||||||
|
<nav>
|
||||||
|
<a href="index.html#about">About</a>
|
||||||
|
<!-- <a href="index.html#case-studies">Case Studies</a> -->
|
||||||
|
<a href="index.html#projects">Work</a>
|
||||||
|
<a href="contact.html">Connect</a>
|
||||||
|
<a href="faq.html">FAQ</a>
|
||||||
|
<a href="index.html#team">Crew</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
const overlay=document.createElement('div');
|
||||||
|
overlay.id='loader-overlay';
|
||||||
|
overlay.innerHTML='<div class="spinner"></div>';
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
window.addEventListener('load',()=>{overlay.classList.add('hide');setTimeout(()=>overlay.remove(),700);});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<!-- Inline styles moved to css/styles.css -->
|
||||||
|
|
||||||
|
<!-- CONTACT SECTION -->
|
||||||
|
<section id="contact" class="section">
|
||||||
|
<h2>CONNECT</h2>
|
||||||
|
<form id="contactForm" action="https://api.web3forms.com/submit" method="POST" autocomplete="on">
|
||||||
|
<input type="hidden" name="access_key" value="cf89ee88-fb13-4091-ac7c-96249aa34eb0">
|
||||||
|
<input type="hidden" name="subject" value="New Contact Form Submission from Biohazard VFX">
|
||||||
|
<input type="hidden" name="from_name" id="from_name">
|
||||||
|
<input type="hidden" name="formatted_message" id="formatted_message">
|
||||||
|
<!-- Honeypot field for spam protection -->
|
||||||
|
<input type="text" name="website" class="honeypot" tabindex="-1" autocomplete="off">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-col">
|
||||||
|
<label for="first_name">First Name</label>
|
||||||
|
<input type="text" id="first_name" name="first_name" placeholder="First Name" required aria-required="true">
|
||||||
|
</div>
|
||||||
|
<div class="form-col">
|
||||||
|
<label for="last_name">Last Name</label>
|
||||||
|
<input type="text" id="last_name" name="last_name" placeholder="Last Name" required aria-required="true">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="email" id="email" name="email" placeholder="Your Email" required aria-required="true">
|
||||||
|
<label for="user_subject">Subject</label>
|
||||||
|
<input type="text" id="user_subject" name="user_subject" placeholder="Subject" required aria-required="true">
|
||||||
|
<label for="message">Message</label>
|
||||||
|
<textarea id="message" name="message" rows="6" placeholder="Yap yap yap... spit your truth, we're all ears." required aria-required="true"></textarea>
|
||||||
|
<button type="submit">Send Message</button>
|
||||||
|
<div id="formMessage" class="form-message"></div>
|
||||||
|
<div class="contact-info">
|
||||||
|
<p>We usually reply within 24 hours.<br>Email: <a href="mailto:contact@biohazardvfx.com">contact@biohazardvfx.com</a></p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
<div id="socials-placeholder"></div>
|
||||||
|
<script>
|
||||||
|
// Inject nav fragment
|
||||||
|
fetch('fragments/nav.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('nav-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Inject socials fragment
|
||||||
|
fetch('fragments/socials.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('socials-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Custom feedback for form submission
|
||||||
|
const form = document.getElementById('contactForm');
|
||||||
|
const msg = document.getElementById('formMessage');
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
msg.style.display = 'none';
|
||||||
|
msg.className = 'form-message';
|
||||||
|
});
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
// Compose clean message for Web3Forms
|
||||||
|
const first = form.first_name.value.trim();
|
||||||
|
const last = form.last_name.value.trim();
|
||||||
|
const email = form.email.value.trim();
|
||||||
|
const subject = form.user_subject.value.trim();
|
||||||
|
const message = form.message.value.trim();
|
||||||
|
form.from_name.value = `${first} ${last}`;
|
||||||
|
form.formatted_message.value =
|
||||||
|
`Name: ${first} ${last}\nEmail: ${email}\nSubject: ${subject}\nMessage:\n${message}`;
|
||||||
|
const formData = new FormData(form);
|
||||||
|
fetch(form.action, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if(data.success) {
|
||||||
|
msg.textContent = 'Thank you! Your message has been sent.';
|
||||||
|
msg.classList.add('success');
|
||||||
|
msg.style.display = 'block';
|
||||||
|
form.reset();
|
||||||
|
} else {
|
||||||
|
msg.textContent = 'There was an error sending your message. Please try again.';
|
||||||
|
msg.classList.add('error');
|
||||||
|
msg.style.display = 'block';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
msg.textContent = 'There was an error sending your message. Please try again.';
|
||||||
|
msg.classList.add('error');
|
||||||
|
msg.style.display = 'block';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
COPYRIGHT
|
||||||
|
(static content, copyright notice)
|
||||||
|
======================================================= -->
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Biohazard VFX. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
<script src="js/ui.js" defer></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
156
crew.html
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Crew — Biohazard VFX</title>
|
||||||
|
<link rel="stylesheet" href="css/styles.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#team.content-block {
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding: 12vh var(--gutter-x) 8vh;
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
color: var(--acid);
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
border-bottom: 1px solid var(--graphite);
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
.team-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2.5rem;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 3.5rem;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
background: var(--graphite);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 14px rgba(0,0,0,0.38);
|
||||||
|
padding: 2.2rem 2rem 1.5rem;
|
||||||
|
font-size: 1.08rem;
|
||||||
|
border: 1px solid rgba(80,80,80,0.18);
|
||||||
|
color: var(--acid);
|
||||||
|
text-align: center;
|
||||||
|
width: 270px;
|
||||||
|
transition: box-shadow .25s, background .25s, transform .2s;
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.portrait:hover {
|
||||||
|
box-shadow: 0 10px 28px rgba(0,0,0,0.5);
|
||||||
|
background: var(--slate);
|
||||||
|
transform: translateY(-4px) scale(1.03);
|
||||||
|
}
|
||||||
|
.portrait img {
|
||||||
|
width: 170px;
|
||||||
|
height: 170px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 18px;
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
/* Remove border for a cleaner look */
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,0.18);
|
||||||
|
border: none;
|
||||||
|
background: #181818;
|
||||||
|
}
|
||||||
|
.portrait h3 {
|
||||||
|
font-family: "ArchiveCond", sans-serif;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--hot);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.portrait p {
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--acid);
|
||||||
|
opacity: 0.88;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.team-container {
|
||||||
|
gap: 1.2rem;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
#team.content-block {
|
||||||
|
padding: 7vh var(--gutter-x) 3vh;
|
||||||
|
}
|
||||||
|
.team-container {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.1rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
padding: 1.2rem 0.7rem 0.7rem;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
.portrait h3 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="js/main.js" defer></script>
|
||||||
|
<div id="nav-placeholder"></div>
|
||||||
|
</section>
|
||||||
|
<section id="team" class="content-block">
|
||||||
|
<h2>Meet the Crew</h2>
|
||||||
|
<div class="team-container">
|
||||||
|
<a class="portrait" href="https://www.instagram.com/nicholai.exe/" target="_blank" aria-label="Nicholai Vogel Instagram">
|
||||||
|
<img src="images/nicholai.jpg" alt="Nicholai Vogel">
|
||||||
|
<h3>Nicholai Vogel</h3>
|
||||||
|
<p>Founder & CEO<br>VFX & CG Supervisor<br><span style="opacity:.7;font-size:.95em">"I just work here."</span></p>
|
||||||
|
</a>
|
||||||
|
<a class="portrait" href="https://www.instagram.com/davaneh/" target="_blank" aria-label="Davane Instagram">
|
||||||
|
<img src="images/davane.jpg" alt="Davane">
|
||||||
|
<h3>DAVANÉ</h3>
|
||||||
|
<p>Co-Founder & Executive Producer<br>VFX Supervisor<br><span style="opacity:.7;font-size:.95em">"The Executive"</span></p>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
<a class="portrait" href="https://www.instagram.com/nuke_fx/" target="_blank" aria-label="Parth Gupta Instagram">
|
||||||
|
<img src="images/parth.jpg" alt="Parth Gupta">
|
||||||
|
<h3>Parth Gupta</h3>
|
||||||
|
<p>Co-Founder & Executive Producer<br>Comp Supervisor<br><span style="opacity:.7;font-size:.95em">"Hates Matchmove"</span></p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<section id="crew" class="content-block">
|
||||||
|
<div class="crew-content">
|
||||||
|
|
||||||
|
<!-- Content dynamically injected -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
<div id="socials-placeholder"></div>
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Biohazard VFX. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
// Inject nav fragment
|
||||||
|
fetch('fragments/nav.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('nav-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Inject socials fragment
|
||||||
|
fetch('fragments/socials.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('socials-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
55
css/home.css
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* ───────── css/home.css ───────── */
|
||||||
|
:root {
|
||||||
|
/* 1 = 100% (default). Adjust as needed. */
|
||||||
|
--font-scale: 1.0;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
font-size: calc(16px * var(--font-scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page-specific layout overrides for index.html */
|
||||||
|
.content-block, .section {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto 4.5rem;
|
||||||
|
padding: 8vh var(--gutter-x) 6vh;
|
||||||
|
background: none;
|
||||||
|
border-radius: 0.7em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
main.grid {
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
gap: 22px;
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.content-block, .section {
|
||||||
|
padding: 5vh 16px 3vh;
|
||||||
|
max-width: 98vw;
|
||||||
|
}
|
||||||
|
main.grid { max-width: 98vw; gap: 14px; }
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.content-block, .section {
|
||||||
|
padding: 3vh 6px 2vh;
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
main.grid { max-width: 100vw; gap: 8px; }
|
||||||
|
}
|
||||||
|
#projects.grid { margin-bottom: 6rem; }
|
||||||
|
#about.content-block,
|
||||||
|
#team.content-block,
|
||||||
|
#crew.content-block {
|
||||||
|
margin-top: 4.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
#pipeline.section {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
footer { margin-top: 1.5rem; padding-bottom: 1.5rem; }
|
||||||
934
css/styles.css
Normal file
@ -0,0 +1,934 @@
|
|||||||
|
/* ───────── css/styles.css ───────── */
|
||||||
|
:root{
|
||||||
|
--gutter-x: 24px; /* (adjusts margins) */
|
||||||
|
--vh:1vh;
|
||||||
|
/* Safety net for JS-less browsers */
|
||||||
|
--header-h: 64px;
|
||||||
|
font-family:'Roboto Condensed',sans-serif;
|
||||||
|
color-scheme:dark;
|
||||||
|
--acid:#ffffff;
|
||||||
|
--slate:#000000;
|
||||||
|
--graphite:#0e0e0e;
|
||||||
|
--hot:#ffffff;
|
||||||
|
--ghost:#777;
|
||||||
|
/* ====== THEME ====== */
|
||||||
|
--accent:#dbdbdb; /* neon-cyan accent */
|
||||||
|
--glass:rgba(14,14,14,.55);/* translucent card surface */
|
||||||
|
--blur:12px; /* backdrop blur strength */
|
||||||
|
}
|
||||||
|
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
|
||||||
|
@media (max-width: 767px){
|
||||||
|
:root{ --gutter-x: 12px; } /* tighter on phones */
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face{
|
||||||
|
font-family:"BiohazardHead";
|
||||||
|
src:url("../fonts/BebasNeue-Regular.woff2") format("woff2"),
|
||||||
|
url("../fonts/BebasNeue-Regular.woff") format("woff");
|
||||||
|
font-weight:400; font-style:normal;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:"Machina";
|
||||||
|
src:url("../fonts/Bebas_Neue/BebasNeue-Regular.ttf") format("opentype");
|
||||||
|
font-weight:700;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:"ArchiveCond";
|
||||||
|
src:url("..//fonts/JetBrainsMono-2.304/fonts/webfonts/JetBrainsMono-Bold.woff2") format("woff2");
|
||||||
|
font-weight:500;
|
||||||
|
}
|
||||||
|
@font-face{
|
||||||
|
font-family:"SpaceMono";
|
||||||
|
src:url("../fonts/Space_Mono/SpaceMono-Regular.ttf") format("opentype");
|
||||||
|
font-weight:400;
|
||||||
|
}
|
||||||
|
|
||||||
|
body{
|
||||||
|
|
||||||
|
background:var(--slate);color:var(--acid);
|
||||||
|
padding-top: calc(var(--header-h) + 2vh);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add horizontal padding only to main content blocks, not header */
|
||||||
|
.content-block, .section, main.grid {
|
||||||
|
padding-left: var(--gutter-x);
|
||||||
|
padding-right: var(--gutter-x);
|
||||||
|
}
|
||||||
|
|
||||||
|
a{text-decoration:none;color:inherit}
|
||||||
|
img{max-width:100%;display:block}
|
||||||
|
|
||||||
|
/* Fix 100 vh on mobile */
|
||||||
|
@media(max-width:767px){
|
||||||
|
body,html{height:calc(var(--vh)*100)}
|
||||||
|
}
|
||||||
|
|
||||||
|
header.nav{
|
||||||
|
position:fixed;top:0;left:0;width:100vw;/* Ensure full viewport width */
|
||||||
|
z-index:100;
|
||||||
|
display:flex;flex-direction:column;align-items:center;
|
||||||
|
background:linear-gradient(to bottom, rgba(11,11,12,0.6) 0%, rgba(11,11,12,0) 100%);
|
||||||
|
text-shadow:0 0 4px var(--acid);
|
||||||
|
mix-blend-mode:normal;
|
||||||
|
font-size:clamp(14px,1.1vw,20px);
|
||||||
|
margin:0;
|
||||||
|
box-sizing:border-box;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.nav nav{
|
||||||
|
display:flex; gap:24px;
|
||||||
|
font-family:'ArchiveCond',sans-serif;
|
||||||
|
}
|
||||||
|
header.nav .logo{
|
||||||
|
font-family:"Machina",sans-serif;
|
||||||
|
font-weight:700; /* bold blocky */
|
||||||
|
font-size:clamp(40.5px,5.6vw,72px); /* 1.125× larger */
|
||||||
|
color:var(--acid);
|
||||||
|
transform:scale(.95);
|
||||||
|
transition:transform .3s, filter .1s;
|
||||||
|
letter-spacing:.07em;
|
||||||
|
text-transform:uppercase;
|
||||||
|
margin-bottom:2px;
|
||||||
|
position:relative; /* for underline */
|
||||||
|
}
|
||||||
|
|
||||||
|
header.nav .logo:hover{
|
||||||
|
transform:scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fancy underline animation for logo */
|
||||||
|
header.nav .logo::after{
|
||||||
|
content:"";
|
||||||
|
position:absolute;left:0;right:0;bottom:0;
|
||||||
|
height:3px;
|
||||||
|
background:var(--accent);
|
||||||
|
transform:scaleX(0);
|
||||||
|
transform-origin:center center;
|
||||||
|
transition:transform .25s;
|
||||||
|
}
|
||||||
|
header.nav .logo:hover::after{
|
||||||
|
transform:scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
header.social{
|
||||||
|
display:flex; gap:24px;
|
||||||
|
font-family:'ArchiveCond',sans-serif;
|
||||||
|
text-shadow:0 0 2px var(--acid);
|
||||||
|
top:0; left:0; width:100%;
|
||||||
|
z-index:100; padding:8px 0 0;
|
||||||
|
display:flex; flex-direction:column; /* stack logo + nav */
|
||||||
|
align-items:center; /* center horizontally */
|
||||||
|
mix-blend-mode:normal;
|
||||||
|
font-size:clamp(14px,1.1vw,20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer{
|
||||||
|
text-align:center;
|
||||||
|
padding:3rem 1rem;
|
||||||
|
font-size:.95rem;
|
||||||
|
opacity:.7;
|
||||||
|
font-family:"SpaceMono",monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#contact {
|
||||||
|
font-family:"SpaceMono",monospace;
|
||||||
|
max-width:1300px;
|
||||||
|
margin:0 auto;
|
||||||
|
padding:9vh var(--gutter-x) 4vh;
|
||||||
|
line-height:1.4;
|
||||||
|
color:var(--acid);
|
||||||
|
text-shadow:0 0 2px var(--acid);
|
||||||
|
border-bottom:1px solid var(--graphite);
|
||||||
|
}
|
||||||
|
#contact form{
|
||||||
|
font-family:"SpaceMono",monospace;
|
||||||
|
max-width:520px;
|
||||||
|
margin:0 auto;
|
||||||
|
display:flex;flex-direction:column;gap:1.5rem;
|
||||||
|
color:var(--acid);
|
||||||
|
background:var(--glass);
|
||||||
|
border:1px solid rgba(255,255,255,.06);
|
||||||
|
box-shadow:0 4px 18px rgba(0,0,0,.4);
|
||||||
|
backdrop-filter:blur(var(--blur));
|
||||||
|
-webkit-backdrop-filter:blur(var(--blur));
|
||||||
|
padding:2rem 1.5rem 1.5rem;
|
||||||
|
border-radius:10px;
|
||||||
|
}
|
||||||
|
#contact label{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
margin-bottom:.4rem;
|
||||||
|
font-weight:500;
|
||||||
|
font-size:1rem;
|
||||||
|
text-align:left;
|
||||||
|
color:var(--accent);
|
||||||
|
letter-spacing:.04em;
|
||||||
|
text-transform:uppercase;
|
||||||
|
}
|
||||||
|
#contact input,
|
||||||
|
#contact textarea{
|
||||||
|
font-family:"SpaceMono",monospace;
|
||||||
|
padding:.75rem 1rem;
|
||||||
|
margin-bottom:.5rem;
|
||||||
|
border:1px solid rgba(255,255,255,.12);
|
||||||
|
background:rgba(0,0,0,.35);
|
||||||
|
color:var(--acid);
|
||||||
|
border-radius:4px;
|
||||||
|
font-size:1rem;
|
||||||
|
transition:border-color .2s, box-shadow .2s;
|
||||||
|
resize:vertical;
|
||||||
|
}
|
||||||
|
#contact input:focus,
|
||||||
|
#contact textarea:focus{
|
||||||
|
border-color:var(--accent);
|
||||||
|
box-shadow:0 0 6px var(--accent);
|
||||||
|
outline:none;
|
||||||
|
}
|
||||||
|
#contact input[type="submit"]{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
cursor:pointer;
|
||||||
|
background:var(--accent);
|
||||||
|
color:var(--slate);
|
||||||
|
font-weight:700;
|
||||||
|
text-transform:uppercase;
|
||||||
|
letter-spacing:.07em;
|
||||||
|
border:none;
|
||||||
|
border-radius:4px;
|
||||||
|
padding:.85rem 1.5rem;
|
||||||
|
transition:background .2s,color .2s,box-shadow .2s;
|
||||||
|
margin-top:.5rem;
|
||||||
|
}
|
||||||
|
#contact input[type="submit"]:hover{
|
||||||
|
background:var(--acid);
|
||||||
|
color:var(--graphite);
|
||||||
|
box-shadow:0 2px 12px rgba(0,0,0,.25);
|
||||||
|
}
|
||||||
|
#contact button[type="submit"]{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
cursor:pointer;
|
||||||
|
background:var(--accent);
|
||||||
|
color:var(--slate);
|
||||||
|
font-weight:700;
|
||||||
|
text-transform:uppercase;
|
||||||
|
letter-spacing:.07em;
|
||||||
|
border:none;
|
||||||
|
border-radius:4px;
|
||||||
|
padding:.85rem 1.5rem;
|
||||||
|
transition:background .2s,color .2s,box-shadow .2s;
|
||||||
|
margin-top:.5rem;
|
||||||
|
}
|
||||||
|
#contact button[type="submit"]:hover{
|
||||||
|
background:var(--acid);
|
||||||
|
color:var(--graphite);
|
||||||
|
box-shadow:0 2px 12px rgba(0,0,0,.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
header.nav a{
|
||||||
|
transition:opacity .3s
|
||||||
|
}
|
||||||
|
|
||||||
|
header.nav a:hover{
|
||||||
|
opacity:.8
|
||||||
|
}
|
||||||
|
|
||||||
|
/* underline kicker */
|
||||||
|
header.nav nav a::after{
|
||||||
|
content:"";
|
||||||
|
display:block;
|
||||||
|
height:2px;
|
||||||
|
background:var(--accent);
|
||||||
|
transform:scaleX(0);transition:transform .25s;
|
||||||
|
}
|
||||||
|
header.nav nav a:hover::after{transform:scaleX(1);}
|
||||||
|
@media(max-width:767px){
|
||||||
|
header.nav{top:8px;justify-content:space-between;padding:0 var(--gutter);font-size:12px}
|
||||||
|
header.nav .logo{margin:0;font-weight:bold}
|
||||||
|
header.nav nav{gap:16px}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GRID */
|
||||||
|
.grid{
|
||||||
|
--cols:4;
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:repeat(var(--cols),1fr);
|
||||||
|
gap:24px;
|
||||||
|
padding-top: calc(var(--header-h)*.5 + 2vh);
|
||||||
|
counter-reset: item; /* Reset Project counter */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width:1024px){.grid{--cols:3}}
|
||||||
|
@media(max-width:767px){.grid{--cols:2;gap:12px;padding:calc(var(--vh)*5) var(--gutter)}}
|
||||||
|
|
||||||
|
.item:not(.hidden) h2::before{ /* only real cards */
|
||||||
|
counter-increment:item;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item{
|
||||||
|
position:relative;
|
||||||
|
counter-increment:item;
|
||||||
|
overflow:hidden;cursor:pointer;
|
||||||
|
opacity:0;transform:translateY(40px);
|
||||||
|
transition:opacity .6s,transform .6s;
|
||||||
|
}
|
||||||
|
.item.loaded{opacity:1;transform:none}
|
||||||
|
|
||||||
|
|
||||||
|
/* Item Spans */
|
||||||
|
|
||||||
|
.item[data-span="banner"] { grid-column: span 3; }
|
||||||
|
.item[data-span="portrait-xl"]{ grid-row: span 3; }
|
||||||
|
.item[data-span="wide"] { grid-column: span 2; }
|
||||||
|
.item[data-span="tall"] { grid-row: span 2; }
|
||||||
|
.item[data-span="big"] { grid-column: span 2; grid-row: span 2; }
|
||||||
|
|
||||||
|
@media(max-width:767px){
|
||||||
|
.item[data-span="wide"],
|
||||||
|
.item[data-span="tall"],
|
||||||
|
.item[data-span="big"]{ grid-column: span 2; grid-row: span 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
small,code,.mono{font-family:"SpaceMono",monospace}
|
||||||
|
|
||||||
|
.item h2{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
letter-spacing:.04em;
|
||||||
|
font-size:clamp(12px,.9vw,18px);
|
||||||
|
text-transform:uppercase;
|
||||||
|
margin-bottom:8px;
|
||||||
|
line-height:1.1;
|
||||||
|
display:inline-block; /* so the counter sits inline */
|
||||||
|
position:relative;
|
||||||
|
transition:opacity .5s; /* Only opacity transitions */
|
||||||
|
}
|
||||||
|
|
||||||
|
.item h2::before{
|
||||||
|
counter-increment:item;
|
||||||
|
content: counter(item,decimal-leading-zero) " ";
|
||||||
|
font-family:'Roboto Mono',monospace;
|
||||||
|
margin-right:.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Underline kicker for item h2, styled like nav */
|
||||||
|
.item h2::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--accent);
|
||||||
|
transform: scaleX(0);
|
||||||
|
transition: transform .25s;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
.item:hover h2::after {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumb{
|
||||||
|
background-size:cover;background-position:center;
|
||||||
|
padding-bottom:62.5%;
|
||||||
|
transition:opacity .5s,transform .5s
|
||||||
|
}
|
||||||
|
.item:hover .thumb {
|
||||||
|
opacity:.8;
|
||||||
|
transform:scale(1.04);
|
||||||
|
}
|
||||||
|
.item:hover h2{
|
||||||
|
opacity:.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-block{
|
||||||
|
max-width:800px;
|
||||||
|
margin:0 auto;
|
||||||
|
padding:20vh var(--gutter);
|
||||||
|
font-family:'Roboto Mono',monospace;
|
||||||
|
line-height:1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#about{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
max-width:1300px;
|
||||||
|
margin:0 auto;
|
||||||
|
padding:4vh var(--gutter-x) 1vh;
|
||||||
|
line-height:1.2;
|
||||||
|
color:var(--acid);
|
||||||
|
text-shadow:0 0 2px var(--acid);
|
||||||
|
border-bottom:1px solid var(--graphite);
|
||||||
|
}
|
||||||
|
|
||||||
|
#crew{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
max-width:1300px;
|
||||||
|
margin:0 auto;
|
||||||
|
padding:4vh var(--gutter-x) 4vh;
|
||||||
|
line-height:1.2;
|
||||||
|
color:var(--acid);
|
||||||
|
text-shadow:0 0 2px var(--acid);
|
||||||
|
border-bottom:1px solid var(--graphite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PIPELINE SECTION */
|
||||||
|
.section h2{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
font-size:clamp(1.1rem,1.5vw + .5rem,2rem);
|
||||||
|
text-transform:uppercase;
|
||||||
|
letter-spacing:.05em;
|
||||||
|
color:var(--accent);
|
||||||
|
margin:0 0 2.2rem;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
.section h2::after{
|
||||||
|
content:"";
|
||||||
|
position:absolute;left:0;bottom:-6px;
|
||||||
|
width:40px;height:2px;
|
||||||
|
background:var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pipeline{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
padding:4vh var(--gutter-x) 1vh;
|
||||||
|
line-height:1.2;
|
||||||
|
}
|
||||||
|
.pipeline-steps{
|
||||||
|
gap:var(--gutter-x);
|
||||||
|
}
|
||||||
|
.pipeline-step{
|
||||||
|
border-radius:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-step {
|
||||||
|
background: var(--glass);
|
||||||
|
color: var(--acid);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 18px rgba(0,0,0,.4);
|
||||||
|
padding: 2rem 1.5rem 1.5rem;
|
||||||
|
min-width: 220px;
|
||||||
|
max-width: 320px;
|
||||||
|
flex: 1 1 220px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0.97;
|
||||||
|
transition: transform .25s, box-shadow .25s, opacity .25s;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-step:hover {
|
||||||
|
transform: translateY(-4px) scale(1.028);
|
||||||
|
box-shadow: 0 10px 32px rgba(0,0,0,.55);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-step h3 {
|
||||||
|
font-family: "ArchiveCond", sans-serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-top: 0;
|
||||||
|
color: var(--hot);
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-step p,
|
||||||
|
.pipeline-step small {
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
color: var(--acid);
|
||||||
|
opacity: 0.85;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.pipeline-steps {
|
||||||
|
gap: 1.2rem;
|
||||||
|
}
|
||||||
|
.pipeline-step {
|
||||||
|
padding: 1.2rem 0.7rem 1rem;
|
||||||
|
min-width: 160px;
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#pipeline {
|
||||||
|
padding: 6vh var(--gutter-x) 2vh;
|
||||||
|
}
|
||||||
|
.pipeline-steps {
|
||||||
|
gap: 0.7rem;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.pipeline-step {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 320px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TEAM SECTION */
|
||||||
|
#team {
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
max-width: 1300px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 4vh var(--gutter-x) 2.5vh; /* ↓ tighter bottom gap */
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--acid);
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
border-bottom: 1px solid var(--graphite);
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-container {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--gutter-x);
|
||||||
|
margin: 3rem 0; /* slightly more spacing between heading and founder images */
|
||||||
|
padding: 0;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portrait {
|
||||||
|
width: clamp(200px, 22vw, 300px);
|
||||||
|
padding: 0 0 1.25rem;
|
||||||
|
overflow: hidden; /* allow image to bleed */
|
||||||
|
background:var(--glass);
|
||||||
|
color: var(--acid);
|
||||||
|
border-radius: 10px;
|
||||||
|
/* glass-card behaviour */
|
||||||
|
/* duplicate here to avoid extra class */
|
||||||
|
border:1px solid rgba(255,255,255,.06);
|
||||||
|
box-shadow:0 4px 18px rgba(0,0,0,.35);
|
||||||
|
backdrop-filter:blur(var(--blur));
|
||||||
|
-webkit-backdrop-filter:blur(var(--blur));
|
||||||
|
transition:transform .25s, box-shadow .25s, opacity .25s;
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0.96;
|
||||||
|
}
|
||||||
|
.portrait:hover {
|
||||||
|
transform:translateY(-4px) scale(1.03);
|
||||||
|
box-shadow:0 10px 32px rgba(0,0,0,.55);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.portrait img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 0;
|
||||||
|
object-fit: cover;
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.18);
|
||||||
|
background: var(--slate);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.portrait h3 {
|
||||||
|
font-family: "ArchiveCond", sans-serif;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
margin-top: 0;
|
||||||
|
color: var(--hot);
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.portrait p {
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--acid);
|
||||||
|
opacity: 0.85;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.team-container {
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
width: 180px;
|
||||||
|
padding: 1rem 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.portrait img {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#team {
|
||||||
|
padding: 6vh var(--gutter-x) 2vh;
|
||||||
|
}
|
||||||
|
.team-container {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 320px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.portrait img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header.social social a {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
transition: opacity .3s;
|
||||||
|
}
|
||||||
|
header.social social a::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--acid);
|
||||||
|
transform: scaleX(0);
|
||||||
|
transition: transform 0.25s cubic-bezier(.4,0,.2,1);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
header.social social a:hover::after {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- GLOBAL GLASS CARD -------- */
|
||||||
|
.glass-card{
|
||||||
|
background:var(--glass);
|
||||||
|
border:1px solid rgba(255,255,255,.06);
|
||||||
|
box-shadow:0 4px 18px rgba(0,0,0,.4);
|
||||||
|
backdrop-filter:blur(var(--blur));
|
||||||
|
-webkit-backdrop-filter:blur(var(--blur));
|
||||||
|
transition:transform .25s, box-shadow .25s, opacity .25s;
|
||||||
|
}
|
||||||
|
.glass-card:hover{
|
||||||
|
transform:translateY(-4px) scale(1.025);
|
||||||
|
box-shadow:0 10px 32px rgba(0,0,0,.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- SECTION HEADING -------- */
|
||||||
|
.content-block h2{
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
font-size:clamp(1.1rem,1.5vw + .5rem,2rem);
|
||||||
|
text-transform:uppercase;
|
||||||
|
letter-spacing:.05em;
|
||||||
|
color:var(--accent);
|
||||||
|
margin:0 0 2.2rem;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
.content-block h2::after{
|
||||||
|
content:"";
|
||||||
|
position:absolute;left:0;bottom:-6px;
|
||||||
|
width:40px;height:2px;
|
||||||
|
background:var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== NAV accent underline ===== */
|
||||||
|
header.nav nav a::after{
|
||||||
|
background:var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== GRID card underline ===== */
|
||||||
|
.item h2::after {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== PORTRAITS ===== */
|
||||||
|
.portrait {
|
||||||
|
width: clamp(200px, 22vw, 300px);
|
||||||
|
padding: 0 0 1.25rem;
|
||||||
|
overflow: hidden; /* allow image to bleed */
|
||||||
|
background:var(--glass);
|
||||||
|
color: var(--acid);
|
||||||
|
border-radius: 10px;
|
||||||
|
/* glass-card behaviour */
|
||||||
|
/* duplicate here to avoid extra class */
|
||||||
|
border:1px solid rgba(255,255,255,.06);
|
||||||
|
box-shadow:0 4px 18px rgba(0,0,0,.35);
|
||||||
|
backdrop-filter:blur(var(--blur));
|
||||||
|
-webkit-backdrop-filter:blur(var(--blur));
|
||||||
|
transition:transform .25s, box-shadow .25s, opacity .25s;
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0.96;
|
||||||
|
}
|
||||||
|
.portrait:hover {
|
||||||
|
transform:translateY(-4px) scale(1.03);
|
||||||
|
box-shadow:0 10px 32px rgba(0,0,0,.55);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.portrait img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 0;
|
||||||
|
object-fit: cover;
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.18);
|
||||||
|
background: var(--slate);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.portrait h3 {
|
||||||
|
font-family: "ArchiveCond", sans-serif;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
margin-top: 0;
|
||||||
|
color: var(--hot);
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.portrait p {
|
||||||
|
font-family:"ArchiveCond",sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--acid);
|
||||||
|
opacity: 0.85;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.team-container {
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
width: 180px;
|
||||||
|
padding: 1rem 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.portrait img {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#team {
|
||||||
|
padding: 6vh var(--gutter-x) 2vh;
|
||||||
|
}
|
||||||
|
.team-container {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.portrait {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 320px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.portrait img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header.social social a {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
transition: opacity .3s;
|
||||||
|
}
|
||||||
|
header.social social a::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--acid);
|
||||||
|
transform: scaleX(0);
|
||||||
|
transition: transform 0.25s cubic-bezier(.4,0,.2,1);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
header.social social a:hover::after {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== PIPELINE, FAQ, PROJECT GLASS ===== */
|
||||||
|
.faq-list details,
|
||||||
|
.project-section{
|
||||||
|
background:var(--glass);
|
||||||
|
border:1px solid rgba(255,255,255,.06);
|
||||||
|
box-shadow:0 4px 18px rgba(0,0,0,.38);
|
||||||
|
backdrop-filter:blur(var(--blur));
|
||||||
|
-webkit-backdrop-filter:blur(var(--blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Contact info */
|
||||||
|
.contact-info{
|
||||||
|
margin:2em 0 4vh;text-align:center;font-size:1rem;opacity:.7;
|
||||||
|
font-family:"SpaceMono",monospace;
|
||||||
|
}
|
||||||
|
.contact-info a{color:var(--accent);}
|
||||||
|
|
||||||
|
/* === BACK TO TOP BUTTON === */
|
||||||
|
.back-to-top {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2rem;
|
||||||
|
right: 2rem;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1000;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateY(20px);
|
||||||
|
transition: opacity .3s, transform .3s, background .3s;
|
||||||
|
|
||||||
|
/* Glass card style */
|
||||||
|
background: var(--glass);
|
||||||
|
border: 1px solid rgba(255,255,255,.06);
|
||||||
|
backdrop-filter: blur(var(--blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-to-top.visible {
|
||||||
|
opacity: .8;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-to-top:hover {
|
||||||
|
opacity: 1;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-to-top svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
stroke: var(--accent);
|
||||||
|
transition: stroke .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-to-top:hover svg {
|
||||||
|
stroke: var(--slate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === PAGE LOADER === */
|
||||||
|
#loader-overlay{
|
||||||
|
position:fixed;inset:0;
|
||||||
|
background:rgba(0,0,0,.85);
|
||||||
|
backdrop-filter:blur(6px);
|
||||||
|
display:flex;align-items:center;justify-content:center;
|
||||||
|
z-index:9999;
|
||||||
|
transition:opacity .6s;
|
||||||
|
}
|
||||||
|
#loader-overlay.hide{opacity:0;pointer-events:none;}
|
||||||
|
.spinner{
|
||||||
|
width:72px;height:72px;
|
||||||
|
border:6px solid rgba(255,255,255,.15);
|
||||||
|
border-top-color:var(--accent);
|
||||||
|
border-radius:50%;
|
||||||
|
animation:spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin{to{transform:rotate(360deg);}}
|
||||||
|
|
||||||
|
/* ===== HERO PARALLAX REFINEMENT ===== */
|
||||||
|
.hero{
|
||||||
|
position:relative;
|
||||||
|
height:100vh;
|
||||||
|
margin-top:-15vh; /* Remove negative margin to bring video to top */
|
||||||
|
overflow:hidden;
|
||||||
|
z-index:1;
|
||||||
|
}
|
||||||
|
/* Video as moveable box that behaves as background */
|
||||||
|
.hero video{
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
width:100%;
|
||||||
|
height:120vh;
|
||||||
|
object-fit:cover;
|
||||||
|
z-index:-2;
|
||||||
|
pointer-events:none;
|
||||||
|
transform:translateY(-100vh);
|
||||||
|
transition:transform 0.1s ease-out;
|
||||||
|
}
|
||||||
|
/* Gradient overlay - separate from video, controlled by scroll */
|
||||||
|
.hero::after{
|
||||||
|
content:"";
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
width:100%;
|
||||||
|
height:200vh; /* further extend to mask bottom edge even during video movement */
|
||||||
|
pointer-events:none;
|
||||||
|
background:linear-gradient(to bottom,rgba(0,0,0,0) 0%,rgba(0,0,0,0.5) 50%,rgba(0,0,0,1) 100%);
|
||||||
|
z-index:-1;
|
||||||
|
opacity:var(--gradient-opacity,100%);
|
||||||
|
transition:opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dynamic about section padding */
|
||||||
|
#about.content-block {
|
||||||
|
margin-top: 15vh; /* Start with large padding */
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
transition: margin-top 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When about reaches top, reduce padding */
|
||||||
|
#about.content-block.reduced-padding {
|
||||||
|
margin-top: -50rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Projects grid spacing */
|
||||||
|
#projects.grid {
|
||||||
|
margin-bottom: 4.5rem;
|
||||||
|
transition: margin-top 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content that scrolls over the video */
|
||||||
|
.content-wrapper {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove black background from content sections */
|
||||||
|
.content-block, .section, main.grid {
|
||||||
|
background: transparent;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== CONTACT FORM REFINEMENTS ===== */
|
||||||
|
.form-message{
|
||||||
|
margin-top:1em;
|
||||||
|
padding:1em;
|
||||||
|
border-radius:6px;
|
||||||
|
font-family:"SpaceMono",monospace;
|
||||||
|
font-size:1.1em;
|
||||||
|
text-align:center;
|
||||||
|
display:none; /* initially hidden */
|
||||||
|
}
|
||||||
|
.form-message.success{background:#1a3d1a;color:#b6ffb6;border:1px solid #3c6;}
|
||||||
|
.form-message.error {background:#3d1a1a;color:#ffb6b6;border:1px solid #c66;}
|
||||||
|
.honeypot{display:none;}
|
||||||
|
|
||||||
|
/* Form layout helpers */
|
||||||
|
.form-row{display:flex;gap:1em;flex-wrap:wrap;}
|
||||||
|
.form-col{flex:1 1 120px;min-width:120px;}
|
||||||
|
|
||||||
|
@media(max-width:600px){
|
||||||
|
#contact{padding:4vh 8px 2vh;font-size:1rem;}
|
||||||
|
#contact form label,
|
||||||
|
#contact form input,
|
||||||
|
#contact form textarea{font-size:1em;}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
259
directory-layout.txt
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
BiohazardWeb/
|
||||||
|
├─ index.html
|
||||||
|
├─ css
|
||||||
|
│ ├─ styles.css
|
||||||
|
├─ fonts/
|
||||||
|
├─ images/
|
||||||
|
├─ js
|
||||||
|
│ ├─ main.js
|
||||||
|
├─ projects/
|
||||||
|
│ ├─ 01_PROJ/
|
||||||
|
│ │ ├─ Videos/
|
||||||
|
│ │ ├─ thumbnail.jpg
|
||||||
|
│ │ ├─ info.txt
|
||||||
|
│ │ ├─ credits.txt
|
||||||
|
├─ reel/
|
||||||
|
│ ├─ Reel_July_2025.mp4
|
||||||
|
├─ written/
|
||||||
|
│ ├─ about_us.txt
|
||||||
|
|
||||||
|
A few notes:
|
||||||
|
1. The blurring on the header looks weird, maybe add a black gradient to it so things fade as it scrolls past them?
|
||||||
|
1.1 the inversion of the text looks weird
|
||||||
|
2. when I hover over the projects I'd like if the text on them transformed with them
|
||||||
|
2.2 the counter is offset, there are only two projects on the page currently and its starting at 02 and skipping 03 and going straight to 04 for the second One
|
||||||
|
3. I'd like if the about section had more room on the left and right
|
||||||
|
3.1 the old about is still at the bottom of the page and needs to be removed
|
||||||
|
4. I'd like to add a case studies section (added to nav as well)
|
||||||
|
4.1 I had a case studies section at one point, you can get an idea of it from this:
|
||||||
|
|
||||||
|
HTML
|
||||||
|
/*==================================================
|
||||||
|
CASE STUDIES
|
||||||
|
==================================================*/
|
||||||
|
#case-studies {
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.case-studies-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 2.5rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.case-study {
|
||||||
|
flex: 0 0 404px;
|
||||||
|
background: var(--box-bg);
|
||||||
|
border: none; /* Remove border for minimalism */
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
aspect-ratio: 404 / 316;
|
||||||
|
}
|
||||||
|
.case-study:hover {
|
||||||
|
transform: translateY(-8px) scale(1.02); /* Slight zoom */
|
||||||
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- CASE STUDIES -->
|
||||||
|
<section id="case-studies" class="section">
|
||||||
|
<h2>CASE STUDIES</h2>
|
||||||
|
<div class="case-studies-container">
|
||||||
|
<div class="case-study">
|
||||||
|
<iframe src="https://www.behance.net/embed/project/211902289?ilo0=1" height="316" width="404" allowfullscreen lazyload frameborder="0" allow="clipboard-write" refererPolicy="strict-origin-when-cross-origin"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="case-study">
|
||||||
|
<iframe src="https://www.behance.net/embed/project/207065443?ilo0=1" height="316" width="404" allowfullscreen lazyload frameborder="0" allow="clipboard-write" refererPolicy="strict-origin-when-cross-origin"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="case-study">
|
||||||
|
<iframe src="https://www.behance.net/embed/project/214528309?ilo0=1" height="316" width="404" allowfullscreen lazyload frameborder="0" allow="clipboard-write" refererPolicy="strict-origin-when-cross-origin"></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
5. I think it would also be nice to have a section dedicated to the founders, I had a previous version on an older version of the sight,
|
||||||
|
it would look good right above contact, heres an example:
|
||||||
|
|
||||||
|
/*==================================================
|
||||||
|
THE CREW
|
||||||
|
==================================================*/
|
||||||
|
#team {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto 12rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.team-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#team .team-container a.portrait {
|
||||||
|
display: block;
|
||||||
|
width: 350px;
|
||||||
|
height: 400px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
background: var(--box-bg);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
#team .team-container a.portrait:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
#team .team-container a.portrait img {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border: 3px solid var(--accent);
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
#team .team-container a.portrait:hover img {
|
||||||
|
border-color: var(--hover-accent); /* Change border on hover */
|
||||||
|
}
|
||||||
|
#team .team-container a.portrait h3 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
#team .team-container a.portrait p {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-style: italic;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- THE CREW -->
|
||||||
|
<section id="team" class="section">
|
||||||
|
<h2>THE CREW</h2>
|
||||||
|
<div class="team-container">
|
||||||
|
<a class="portrait" href="https://www.instagram.com/nicholai.exe/" target="_blank">
|
||||||
|
<img src="assets/nicholai.jpg" alt="Nicholai Vogel" />
|
||||||
|
<h3>Nicholai Vogel</h3>
|
||||||
|
<p>Founder & CEO<br>VFX Supervisor & Lead Generalist (9 yrs)</p>
|
||||||
|
</a>
|
||||||
|
<a class="portrait" href="https://www.instagram.com/nuke_fx/" target="_blank">
|
||||||
|
<img src="assets/parth.jpg" alt="Parth Gupta" />
|
||||||
|
<h3>Parth Gupta</h3>
|
||||||
|
<p>Head of Production<br>Lead Compositor & Coordinator<br><em>(Partner, 3 yrs)</em></p>
|
||||||
|
</a>
|
||||||
|
<a class="portrait" href="https://www.instagram.com/davaneh/" target="_blank">
|
||||||
|
<img src="assets/davane.jpg" alt="Davane" />
|
||||||
|
<h3>Davane</h3>
|
||||||
|
<p>Executive Producer<br>Compositor<br><em>(Partner, 1 yr)</em></p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
6. I think it would also be good to revamp the contact section, right now its too barebones, something like this would be cool but maybe as its own separate page actually, I don't want this one to get too long.
|
||||||
|
HTML
|
||||||
|
/*==================================================
|
||||||
|
CONTACT SECTION
|
||||||
|
==================================================*/
|
||||||
|
#contact { text-align: center; }
|
||||||
|
#contact form {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
#contact label {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
#contact input,
|
||||||
|
#contact textarea {
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1.75rem;
|
||||||
|
border: none;
|
||||||
|
background: var(--contact-bg);
|
||||||
|
color: #FFFFFF;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
#contact input:focus,
|
||||||
|
#contact textarea:focus {
|
||||||
|
background: var(--contact-focus-bg);
|
||||||
|
box-shadow: 0 0 5px var(--accent); /* Glow effect */
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#contact input[type="submit"] {
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--accent);
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
#contact input[type="submit"]:hover {
|
||||||
|
background: var(--contact-hover-bg);
|
||||||
|
}
|
||||||
|
.social-bar {
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.social-bar a {
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-decoration: none;
|
||||||
|
margin: 0 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
.social-bar a:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- CONTACT SECTION -->
|
||||||
|
<section id="contact" class="section">
|
||||||
|
<h2>CONNECT</h2>
|
||||||
|
<form>
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" id="name" placeholder="Your Name" />
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="email" id="email" placeholder="Your Email" />
|
||||||
|
<label for="message">Message</label>
|
||||||
|
<textarea id="message" rows="6" placeholder="Yap yap yap... spit your truth, we're all ears."></textarea>
|
||||||
|
<input type="submit" value="Send Message" />
|
||||||
|
</form>
|
||||||
|
<div class="social-bar">
|
||||||
|
<a href="https://www.instagram.com/biohazardvfx/" target="_blank">Instagram</a>
|
||||||
|
<a href="https://vimeo.com/" target="_blank">Vimeo</a>
|
||||||
|
<a href="https://youtube.com/" target="_blank">YouTube</a>
|
||||||
|
<a href="https://www.behance.net/" target="_blank">Behance</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
7. Lastly, I think we should add a nice little footer.
|
||||||
|
HTML:
|
||||||
|
/*==================================================
|
||||||
|
FOOTER
|
||||||
|
==================================================*/
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
} /*==================================================
|
||||||
|
FOOTER
|
||||||
|
==================================================*/
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Biohazard VFX. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
158
faq.html
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>FAQ — Biohazard VFX</title>
|
||||||
|
<link rel="stylesheet" href="css/styles.css">
|
||||||
|
<style>
|
||||||
|
#faq.content-block {
|
||||||
|
max-width: 2000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 12vh var(--gutter-x) 8vh;
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
color: var(--acid);
|
||||||
|
text-shadow: 0 0 2px var(--acid);
|
||||||
|
border-bottom: 1px solid var(--graphite);
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
.faq-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2.2rem;
|
||||||
|
margin-top: 3.5rem;
|
||||||
|
}
|
||||||
|
.faq-list details {
|
||||||
|
background: var(--graphite);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 14px rgba(0,0,0,0.38);
|
||||||
|
padding: 1.5rem 1.5rem 1.2rem;
|
||||||
|
font-size: 1.08rem;
|
||||||
|
transition: box-shadow .25s, background .25s;
|
||||||
|
border: 1px solid rgba(80,80,80,0.18);
|
||||||
|
position: relative;
|
||||||
|
color: var(--acid);
|
||||||
|
}
|
||||||
|
.faq-list details[open] {
|
||||||
|
box-shadow: 0 10px 28px rgba(0,0,0,0.5);
|
||||||
|
background: var(--slate);
|
||||||
|
}
|
||||||
|
.faq-list summary {
|
||||||
|
font-family: "ArchiveCond", sans-serif;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--hot);
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
margin-bottom: 0.7rem;
|
||||||
|
transition: color .2s;
|
||||||
|
padding: 0.2em 0;
|
||||||
|
}
|
||||||
|
.faq-list details[open] summary {
|
||||||
|
color: var(--acid);
|
||||||
|
}
|
||||||
|
.faq-list p {
|
||||||
|
font-family: "SpaceMono", monospace;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--acid);
|
||||||
|
opacity: 0.88;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
}
|
||||||
|
.faq-list details[open] > .faq-anim {
|
||||||
|
max-height: 500px;
|
||||||
|
opacity: 1;
|
||||||
|
transition: max-height 0.5s cubic-bezier(.4,0,.2,1), opacity 0.4s;
|
||||||
|
}
|
||||||
|
.faq-anim {
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: max-height 0.5s cubic-bezier(.4,0,.2,1), opacity 0.4s;
|
||||||
|
will-change: max-height, opacity;
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#faq.content-block {
|
||||||
|
padding: 7vh var(--gutter-x) 3vh;
|
||||||
|
}
|
||||||
|
.faq-list {
|
||||||
|
gap: 1.1rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
.faq-list details {
|
||||||
|
padding: 1rem 0.7rem 0.7rem;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
.faq-list summary {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
const overlay=document.createElement('div');
|
||||||
|
overlay.id='loader-overlay';
|
||||||
|
overlay.innerHTML='<div class="spinner"></div>';
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
window.addEventListener('load',()=>{overlay.classList.add('hide');setTimeout(()=>overlay.remove(),700);});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<div id="nav-placeholder"></div>
|
||||||
|
<section id="faq" class="content-block">
|
||||||
|
<h2>Frequently Asked Questions</h2>
|
||||||
|
<div class="faq-list" id="faq-list"></div>
|
||||||
|
</section>
|
||||||
|
<div id="socials-placeholder"></div>
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Biohazard VFX. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
<script src="js/ui.js" defer></script>
|
||||||
|
<script>
|
||||||
|
// Inject nav fragment
|
||||||
|
fetch('fragments/nav.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('nav-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Inject socials fragment
|
||||||
|
fetch('fragments/socials.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('socials-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Load FAQ from JSON
|
||||||
|
fetch('written/faq.json')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(faqs => {
|
||||||
|
const list = document.getElementById('faq-list');
|
||||||
|
list.innerHTML = faqs.map(faq => `
|
||||||
|
<details>
|
||||||
|
<summary>${faq.question}</summary>
|
||||||
|
<div class="faq-anim"><p>${faq.answer}</p></div>
|
||||||
|
</details>
|
||||||
|
`).join('');
|
||||||
|
// Only one FAQ open at a time, and animate dropdowns smoothly
|
||||||
|
document.querySelectorAll('.faq-list details').forEach((d) => {
|
||||||
|
d.addEventListener('toggle', function(e) {
|
||||||
|
if (d.open) {
|
||||||
|
document.querySelectorAll('.faq-list details').forEach((other) => {
|
||||||
|
if (other !== d && other.open) other.removeAttribute('open');
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
const newRect = d.getBoundingClientRect();
|
||||||
|
const isFullyVisible = newRect.top >= 0 && newRect.bottom <= window.innerHeight;
|
||||||
|
if (!isFullyVisible) {
|
||||||
|
d.scrollIntoView({behavior: 'smooth', block: 'start'});
|
||||||
|
}
|
||||||
|
}, 520);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
fonts/Archiv-Grotesk-Font.zip
Normal file
BIN
fonts/Archiv-Grotesk-Font/ArchivGroteskTrial-RegularTrial.otf
Normal file
BIN
fonts/Bebas_Neue,Instrument_Serif,Space_Mono.zip
Normal file
BIN
fonts/Bebas_Neue,Space_Mono.zip
Normal file
BIN
fonts/Bebas_Neue/BebasNeue-Regular.ttf
Normal file
93
fonts/Bebas_Neue/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright © 2010 by Dharma Type.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://openfontlicense.org
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
fonts/Instrument_Serif/InstrumentSerif-Italic.ttf
Normal file
BIN
fonts/Instrument_Serif/InstrumentSerif-Regular.ttf
Normal file
93
fonts/Instrument_Serif/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://openfontlicense.org
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
fonts/JetBrainsMono-2.304.zip
Normal file
10
fonts/JetBrainsMono-2.304/AUTHORS.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# This is the official list of project authors for copyright purposes.
|
||||||
|
# This file is distinct from the CONTRIBUTORS.txt file.
|
||||||
|
# See the latter for an explanation.
|
||||||
|
#
|
||||||
|
# Names should be added to this file as:
|
||||||
|
# Name or Organization <email address>
|
||||||
|
|
||||||
|
JetBrains <>
|
||||||
|
Philipp Nurullin <philipp.nurullin@jetbrains.com>
|
||||||
|
Konstantin Bulenkov <kb@jetbrains.com>
|
||||||
93
fonts/JetBrainsMono-2.304/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Bold.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-BoldItalic.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-ExtraBold.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-ExtraLight.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Italic.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Light.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Medium.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Regular.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-SemiBold.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Thin.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-ThinItalic.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMonoNL-Bold.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMonoNL-Italic.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMonoNL-Light.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMonoNL-Medium.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMonoNL-Regular.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMonoNL-SemiBold.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/ttf/JetBrainsMonoNL-Thin.ttf
Normal file
BIN
fonts/JetBrainsMono-2.304/fonts/variable/JetBrainsMono[wght].ttf
Normal file
BIN
fonts/Space_Mono.zip
Normal file
93
fonts/Space_Mono/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2016 The Space Mono Project Authors (https://github.com/googlefonts/spacemono)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://openfontlicense.org
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
fonts/Space_Mono/SpaceMono-Bold.ttf
Normal file
BIN
fonts/Space_Mono/SpaceMono-BoldItalic.ttf
Normal file
BIN
fonts/Space_Mono/SpaceMono-Italic.ttf
Normal file
BIN
fonts/Space_Mono/SpaceMono-Regular.ttf
Normal file
BIN
fonts/Stretch-Sans-Font.zip
Normal file
BIN
fonts/Stretch-Sans-Font/STRRETCH SANS.otf
Normal file
1414
fonts/Stretch-Sans-Font/STRRETCH SANS.svg
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
fonts/Stretch-Sans-Font/STRRETCH SANS.ttf
Normal file
BIN
fonts/Stretch-Sans-Font/STRRETCH SANS.woff
Normal file
BIN
fonts/Vamos-Font.zip
Normal file
BIN
fonts/Vamos-Font/vamos.otf
Normal file
11
fragments/nav.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<header class="nav">
|
||||||
|
<a href="index.html#top" class="logo">BIOHAZARD VFX</a>
|
||||||
|
<nav>
|
||||||
|
<a href="index.html#about" aria-label="About section">About</a>
|
||||||
|
<!-- <a href="index.html#case-studies">Case Studies</a> -->
|
||||||
|
<a href="index.html#projects" aria-label="Projects section">Work</a>
|
||||||
|
<a href="contact.html" aria-label="Contact page">Connect</a>
|
||||||
|
<a href="faq.html" aria-label="FAQ page">FAQ</a>
|
||||||
|
<a href="index.html#team" aria-label="Team section">Crew</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
7
fragments/socials.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<header class="social">
|
||||||
|
<social>
|
||||||
|
<a href="https://www.instagram.com/biohazardvfx/">Instagram</a>
|
||||||
|
<a href="https://youtube.com/">YouTube</a>
|
||||||
|
<a href="https://www.behance.net/">Behance</a>
|
||||||
|
</social>
|
||||||
|
</header>
|
||||||
BIN
images/Splash.jpg
Normal file
|
After Width: | Height: | Size: 3.6 MiB |
BIN
images/davane.jpg
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
images/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 976 B |
BIN
images/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
images/nicholai-compressed.jpg
Normal file
|
After Width: | Height: | Size: 574 KiB |
BIN
images/nicholai.jpg
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
BIN
images/nicholai.png
Normal file
|
After Width: | Height: | Size: 9.5 MiB |
BIN
images/parth.jpg
Normal file
|
After Width: | Height: | Size: 351 KiB |
312
index.html
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-current="home">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<title>Biohazard VFX | VFX Studio & Post-Production</title>
|
||||||
|
|
||||||
|
<!-- Favicons -->
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png" />
|
||||||
|
|
||||||
|
<!-- SEO Meta -->
|
||||||
|
<meta name="description" content="Biohazard VFX is a global visual-effects studio delivering world-class VFX supervision, 3D animation, and end-to-end post-production." />
|
||||||
|
<!-- canonical -->
|
||||||
|
<link rel="canonical" href="https://biohazardvfx.com/" />
|
||||||
|
|
||||||
|
<!-- Preload critical fonts -->
|
||||||
|
<link rel="preload" href="fonts/BebasNeue-Regular.woff2" as="font" type="font/woff2" crossorigin />
|
||||||
|
<link rel="preload" href="fonts/Space_Mono/SpaceMono-Regular.ttf" as="font" type="font/ttf" crossorigin />
|
||||||
|
<meta name="robots" content="index, follow" />
|
||||||
|
|
||||||
|
<!-- Open Graph / Facebook -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content="Biohazard VFX — Visual Effects Studio" />
|
||||||
|
<meta property="og:description" content="Global visual-effects & post-production studio delivering world-class VFX supervision, 3D animation, and finishing." />
|
||||||
|
<meta property="og:image" content="/images/Splash.jpg" />
|
||||||
|
<meta property="og:url" content="https://biohazardvfx.com/" />
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="Biohazard VFX — Visual Effects Studio" />
|
||||||
|
<meta name="twitter:description" content="Global visual-effects & post-production studio delivering world-class VFX supervision, 3D animation, and finishing." />
|
||||||
|
<meta name="twitter:image" content="/images/Splash.jpg" />
|
||||||
|
|
||||||
|
<!-- Structured Data -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Biohazard VFX",
|
||||||
|
"url": "https://biohazardvfx.com/",
|
||||||
|
"logo": "https://biohazardvfx.com/images/favicon-32x32.png",
|
||||||
|
"contactPoint": [{
|
||||||
|
"@type": "ContactPoint",
|
||||||
|
"email": "contact@biohazardvfx.com",
|
||||||
|
"contactType": "customer service"
|
||||||
|
}],
|
||||||
|
"sameAs": [
|
||||||
|
"https://www.instagram.com/nicholai.exe/",
|
||||||
|
"https://www.instagram.com/davaneh/",
|
||||||
|
"https://www.instagram.com/nuke_fx/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Main stylesheet -->
|
||||||
|
<link rel="stylesheet" href="css/styles.css" />
|
||||||
|
<link rel="stylesheet" href="css/home.css" />
|
||||||
|
<!-- GLOBAL FONT-SIZE CONTROLS ---------------------->
|
||||||
|
<style id="global-font-scale">
|
||||||
|
/*
|
||||||
|
Adjust --font-scale below to make type bigger or smaller everywhere.
|
||||||
|
1 = 100% (default). 1.1 = +10%. 0.9 = −10%, etc.
|
||||||
|
*/
|
||||||
|
:root {
|
||||||
|
--font-scale: 1.0;
|
||||||
|
}
|
||||||
|
html { font-size: calc(16px * var(--font-scale)); }
|
||||||
|
</style>
|
||||||
|
<!-- END FONT-SIZE CONTROLS -->
|
||||||
|
<style>
|
||||||
|
.content-block, .section {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto 4.5rem auto;
|
||||||
|
padding: 8vh var(--gutter-x) 6vh;
|
||||||
|
background: none;
|
||||||
|
border-radius: 0.7em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
main.grid {
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
gap: 22px;
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.content-block, .section {
|
||||||
|
padding: 5vh 16px 3vh;
|
||||||
|
max-width: 98vw;
|
||||||
|
}
|
||||||
|
main.grid {
|
||||||
|
max-width: 98vw;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.content-block, .section {
|
||||||
|
padding: 3vh 6px 2vh;
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
main.grid {
|
||||||
|
max-width: 100vw;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#projects.grid {
|
||||||
|
margin-bottom: 4.5rem;
|
||||||
|
}
|
||||||
|
#about.content-block {
|
||||||
|
margin-top: 4.5rem;
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
#team.content-block {
|
||||||
|
margin-top: 4.5rem;
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
#crew.content-block {
|
||||||
|
margin-top: 4.5rem;
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
#pipeline.section {
|
||||||
|
margin-bottom: 4.5rem;
|
||||||
|
max-width: 2000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
NAV
|
||||||
|
======================================================= -->
|
||||||
|
<div id="nav-placeholder"></div>
|
||||||
|
|
||||||
|
<!-- HERO SECTION -->
|
||||||
|
<section id="hero" class="hero">
|
||||||
|
<video src="videos/reel.mp4" autoplay muted loop playsinline preload="metadata"></video>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
ABOUT
|
||||||
|
======================================================= -->
|
||||||
|
|
||||||
|
<section id="about" class="content-block" data-speed="0.15">
|
||||||
|
<div class="about-content">
|
||||||
|
<!-- Content dynamically injected -->
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
PROJECT GRID (populated dynamically from /projects)
|
||||||
|
======================================================= -->
|
||||||
|
<main id="projects" class="grid" data-speed="0.1"></main>
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
TEAM SECTION (portraits)
|
||||||
|
======================================================= -->
|
||||||
|
<section id="team" class="content-block" data-speed="0.12">
|
||||||
|
<h2>Meet The Founders</h2>
|
||||||
|
<div class="team-container">
|
||||||
|
<a class="portrait" href="https://www.instagram.com/nicholai.exe/" target="_blank" aria-label="Nicholai Vogel Instagram">
|
||||||
|
<img src="images/nicholai.jpg" alt="Nicholai Vogel">
|
||||||
|
<h3>Nicholai Vogel</h3>
|
||||||
|
<p>Founder & CEO<br>VFX & CG Supervisor<br><span style="opacity:.7;font-size:.95em">"I just work here."</span></p>
|
||||||
|
</a>
|
||||||
|
<a class="portrait" href="https://www.instagram.com/davaneh/" target="_blank" aria-label="Davane Instagram">
|
||||||
|
<img src="images/davane.jpg" alt="Davane">
|
||||||
|
<h3>DAVANÉ</h3>
|
||||||
|
<p>Co-Founder & Executive Producer<br>VFX Supervisor<br><span style="opacity:.7;font-size:.95em">"The Executive"</span></p>
|
||||||
|
</a>
|
||||||
|
<a class="portrait" href="https://www.instagram.com/nuke_fx/" target="_blank" aria-label="Parth Gupta Instagram">
|
||||||
|
<img src="images/parth.jpg" alt="Parth Gupta">
|
||||||
|
<h3>Parth Gupta</h3>
|
||||||
|
<p>Co-Founder & Executive Producer<br>Comp Lead<br><span style="opacity:.7;font-size:.95em">"Hates Matchmove"</span></p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
CREW COPY SECTION (text injected from written/crew.txt)
|
||||||
|
======================================================= -->
|
||||||
|
<section id="crew" class="content-block" data-speed="0.14">
|
||||||
|
<div class="crew-content"><!-- Content dynamically injected --></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
Pipeline Section
|
||||||
|
=======================================================
|
||||||
|
<section id="pipeline" class="section">
|
||||||
|
<h2>PIPELINE</h2>
|
||||||
|
<div class="pipeline-content" id="pipeline-content">
|
||||||
|
</div>
|
||||||
|
</section> -->
|
||||||
|
|
||||||
|
|
||||||
|
<div class="contact-info" style="margin-top:2em;text-align:center;font-size:1em;opacity:.7;">
|
||||||
|
<p>We usually reply within 24 hours.<br>Email: <a href="mailto:contact@biohazardvfx.com">contact@biohazardvfx.com</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Main JS bundle (handles lazy-loading + data fetch) -->
|
||||||
|
<script src="js/main.js" defer></script>
|
||||||
|
<script src="js/common.js" defer></script>
|
||||||
|
<script src="js/ui.js" defer></script>
|
||||||
|
<script src="js/home.js" defer></script>
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
Social Links
|
||||||
|
======================================================= -->
|
||||||
|
<div id="socials-placeholder"></div>
|
||||||
|
|
||||||
|
<!-- =======================================================
|
||||||
|
COPYRIGHT
|
||||||
|
(static content, copyright notice)
|
||||||
|
======================================================= -->
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Biohazard VFX. All Rights Reserved.</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Inject nav fragment
|
||||||
|
fetch('fragments/nav.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('nav-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Inject socials fragment
|
||||||
|
fetch('fragments/socials.html')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('socials-placeholder').outerHTML = html;
|
||||||
|
});
|
||||||
|
// Inject pipeline blurb
|
||||||
|
fetch('written/pipeline.txt')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(txt => {
|
||||||
|
document.getElementById('pipeline-content').innerHTML = `<p>${txt}</p>`;
|
||||||
|
});
|
||||||
|
// 2. Smooth scroll navigation for anchor links
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
|
anchor.addEventListener('click', function(e) {
|
||||||
|
const targetId = this.getAttribute('href').slice(1);
|
||||||
|
const target = document.getElementById(targetId);
|
||||||
|
if(target) {
|
||||||
|
e.preventDefault();
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 10. Lazy loading for all images and iframes
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('img, iframe').forEach(el => {
|
||||||
|
if(!el.hasAttribute('loading')) el.setAttribute('loading', 'lazy');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 9. Accessibility: Add ARIA labels to nav links
|
||||||
|
document.querySelectorAll('header.nav nav a').forEach(link => {
|
||||||
|
if(!link.hasAttribute('aria-label')) {
|
||||||
|
link.setAttribute('aria-label', link.textContent.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 11. Parallax video effect
|
||||||
|
document.addEventListener('DOMContentLoaded',()=>{
|
||||||
|
const hero=document.getElementById('hero');
|
||||||
|
const about=document.getElementById('about');
|
||||||
|
const video=hero?.querySelector('video');
|
||||||
|
if(!hero||!about||!video) return;
|
||||||
|
|
||||||
|
const update=()=>{
|
||||||
|
const scrollY=window.scrollY;
|
||||||
|
const viewportHeight=window.innerHeight;
|
||||||
|
const aboutRect=about.getBoundingClientRect();
|
||||||
|
const aboutTop=aboutRect.top;
|
||||||
|
|
||||||
|
// Gradually fade IN gradient as we scroll: 0 when at top, 1 once About approaches hero top
|
||||||
|
const gradientOpacity = Math.max(0, Math.min(1, (viewportHeight - aboutTop) / (viewportHeight * .0125)));
|
||||||
|
hero.style.setProperty('--gradient-opacity', gradientOpacity);
|
||||||
|
|
||||||
|
// Allow the About block to slide upward and sit over the video. We let the margin become
|
||||||
|
// slightly negative for an overlapping effect once the user scrolls further.
|
||||||
|
const aboutPadding=15-(scrollY*0.03); // rem units – can go negative for overlap
|
||||||
|
about.style.marginTop=aboutPadding+'rem';
|
||||||
|
|
||||||
|
// Move video box smoothly - no position switching
|
||||||
|
const videoOffset=Math.max(0,scrollY-viewportHeight/2);
|
||||||
|
video.style.transform=`translateY(${videoOffset}px)`;
|
||||||
|
};
|
||||||
|
|
||||||
|
update();
|
||||||
|
window.addEventListener('scroll',update,{passive:true});
|
||||||
|
window.addEventListener('resize',update,{passive:true});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
41
js/common.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* ───────── js/common.js ───────── */
|
||||||
|
(() => {
|
||||||
|
function injectFragment(id, url) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (!el) return;
|
||||||
|
fetch(url)
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(html => { el.outerHTML = html; })
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Inject shared fragments if placeholders present
|
||||||
|
injectFragment('nav-placeholder', 'fragments/nav.html');
|
||||||
|
injectFragment('socials-placeholder', 'fragments/socials.html');
|
||||||
|
|
||||||
|
// Smooth scroll for same-page anchors
|
||||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
|
anchor.addEventListener('click', e => {
|
||||||
|
const targetId = anchor.getAttribute('href').slice(1);
|
||||||
|
const target = document.getElementById(targetId);
|
||||||
|
if (target) {
|
||||||
|
e.preventDefault();
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lazy-load images & iframes
|
||||||
|
document.querySelectorAll('img, iframe').forEach(el => {
|
||||||
|
if (!el.hasAttribute('loading')) el.setAttribute('loading', 'lazy');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure nav links have aria-labels
|
||||||
|
document.querySelectorAll('header.nav nav a').forEach(link => {
|
||||||
|
if (!link.hasAttribute('aria-label')) {
|
||||||
|
link.setAttribute('aria-label', link.textContent.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
37
js/home.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* ───────── js/home.js ───────── */
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Hero parallax & gradient effect
|
||||||
|
const hero = document.getElementById('hero');
|
||||||
|
const about = document.getElementById('about');
|
||||||
|
const video = hero?.querySelector('video');
|
||||||
|
|
||||||
|
if (hero && about && video) {
|
||||||
|
const update = () => {
|
||||||
|
const scrollY = window.scrollY;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
const aboutTop = about.getBoundingClientRect().top;
|
||||||
|
|
||||||
|
const gradientOpacity = Math.max(0, Math.min(1, (viewportHeight - aboutTop) / (viewportHeight * 0.0125)));
|
||||||
|
hero.style.setProperty('--gradient-opacity', gradientOpacity.toString());
|
||||||
|
|
||||||
|
const aboutPadding = 15 - scrollY * 0.03;
|
||||||
|
about.style.marginTop = `${aboutPadding}rem`;
|
||||||
|
|
||||||
|
const videoOffset = Math.max(0, scrollY - viewportHeight / 2);
|
||||||
|
video.style.transform = `translateY(${videoOffset}px)`;
|
||||||
|
};
|
||||||
|
|
||||||
|
update();
|
||||||
|
window.addEventListener('scroll', update, { passive: true });
|
||||||
|
window.addEventListener('resize', update, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pipeline blurb injection (optional, safe-fail)
|
||||||
|
const pipeTarget = document.getElementById('pipeline-content');
|
||||||
|
if (pipeTarget) {
|
||||||
|
fetch('written/pipeline.txt')
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(txt => { pipeTarget.innerHTML = `<p>${txt}</p>`; })
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
113
js/main.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/* ───────── js/main.js ─────────
|
||||||
|
Dynamically builds the project grid + injects about/contact/crew copy.
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
// Page loader overlay
|
||||||
|
(function(){
|
||||||
|
const overlay=document.createElement('div');
|
||||||
|
overlay.id='loader-overlay';
|
||||||
|
overlay.innerHTML='<div class="spinner"></div>';
|
||||||
|
document.addEventListener('DOMContentLoaded',()=>document.body.appendChild(overlay));
|
||||||
|
window.addEventListener('load',()=>{
|
||||||
|
overlay.classList.add('hide');
|
||||||
|
setTimeout(()=>overlay.remove(),700);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Mobile 100‑vh fix
|
||||||
|
function setVh(){document.documentElement.style.setProperty('--vh',window.innerHeight*0.01);}setVh();window.addEventListener('resize',setVh);
|
||||||
|
|
||||||
|
// Header height fix
|
||||||
|
function setHeaderOffset(){
|
||||||
|
const headerEl = document.querySelector('header.nav');
|
||||||
|
if(!headerEl) return; // Header not in DOM yet – abort and wait
|
||||||
|
const h = headerEl.offsetHeight;
|
||||||
|
document.documentElement.style.setProperty('--header-h', `${h}px`);
|
||||||
|
}
|
||||||
|
setHeaderOffset();
|
||||||
|
// Run once more after a short delay to catch late-injected nav fragments
|
||||||
|
setTimeout(setHeaderOffset, 500);
|
||||||
|
window.addEventListener('resize', setHeaderOffset);
|
||||||
|
|
||||||
|
// Fade‑in intersection observer
|
||||||
|
const reveal = new IntersectionObserver((entries,o)=>{
|
||||||
|
entries.forEach(e=>{if(e.isIntersecting){e.target.classList.add('loaded');o.unobserve(e.target);}});
|
||||||
|
},{threshold:.3});
|
||||||
|
|
||||||
|
// Grab the grid once so every scope has it
|
||||||
|
const grid = document.getElementById('projects');
|
||||||
|
|
||||||
|
// Only run project-grid logic on pages that actually have the grid element
|
||||||
|
if(grid){
|
||||||
|
// Build card element
|
||||||
|
function card({id,title,thumbnail,size='small'}){
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = `project.html?id=${encodeURIComponent(id)}`;
|
||||||
|
a.className = 'item';
|
||||||
|
a.dataset.span = size; // <— MAGIC LINE
|
||||||
|
a.innerHTML = `
|
||||||
|
<h2>${title}</h2>
|
||||||
|
<div class="thumb" style="background-image:url('${thumbnail}')"></div>`;
|
||||||
|
reveal.observe(a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load manifest
|
||||||
|
fetch('projects/projects.json')
|
||||||
|
.then(r=>{if(!r.ok)throw new Error('manifest missing');return r.json();})
|
||||||
|
.then(list=>{
|
||||||
|
if(!Array.isArray(list))throw new Error('manifest must be an array');
|
||||||
|
list.forEach(p=>grid.appendChild(card(p)));
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(err=>{
|
||||||
|
console.error('Manifest issue:',err.message);
|
||||||
|
grid.innerHTML=`<p style="grid-column:1/-1">No projects manifest found.</p>`;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Inject About & Contact copy
|
||||||
|
['about','contact'].forEach(key=>{
|
||||||
|
const el=document.getElementById(key);
|
||||||
|
if(!el)return;
|
||||||
|
fetch(`written/${key}_us.txt`)
|
||||||
|
.then(r=>r.ok?r.text():Promise.reject())
|
||||||
|
.then(txt=>{
|
||||||
|
el.innerHTML=`<h2 style="text-transform:uppercase;margin-bottom:1em">${key}</h2><p>${txt.replace(/\n/g,'<br>')}</p>`;
|
||||||
|
})
|
||||||
|
.catch(()=>{});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inject Crew copy
|
||||||
|
['crew'].forEach(key=>{
|
||||||
|
const el=document.getElementById(key);
|
||||||
|
if(!el)return;
|
||||||
|
fetch(`written/${key}.txt`)
|
||||||
|
.then(r=>r.ok?r.text():Promise.reject())
|
||||||
|
.then(txt=>{
|
||||||
|
el.innerHTML=`<p>${txt.replace(/\n/g,'<br>')}</p>`;
|
||||||
|
})
|
||||||
|
.catch(()=>{});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ───────── SIMPLE PARALLAX HANDLER ─────────
|
||||||
|
// Applies translateY based on scroll position to any element with a data-speed attribute.
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const layers = Array.from(document.querySelectorAll('[data-speed]'));
|
||||||
|
if(!layers.length) return;
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
const scrollY = window.scrollY;
|
||||||
|
layers.forEach(el => {
|
||||||
|
const speed = parseFloat(el.dataset.speed) || 0;
|
||||||
|
// Negative so layers move opposite to scroll for depth illusion
|
||||||
|
el.style.transform = `translateY(${ -scrollY * speed }px)`;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
update();
|
||||||
|
window.addEventListener('scroll', update, { passive: true });
|
||||||
|
window.addEventListener('resize', update, { passive: true });
|
||||||
|
});
|
||||||
|
|
||||||