Rebuild homepage as an editorial page after the VITA reference

Replaces the navy/pink direction with the reference's own language:
paper ground, black ink, hairline rules, Playfair Display for
display, Inter tracked caps for labels, italic serif for dates,
boxed modules, three-column card grid, black rectangular buttons.

Sections in the required order:
1. Latest Tips and Tricks - three clickable cards, category band,
   serif headline, description, reading time, View all tips.
2. Featured Module - Inventory in a bordered plate: manifest facts,
   description, three benefits, Explore module CTA.
3. Latest News from the Community - three rows with date, headline,
   excerpt and source, pulled from the OCA repositories.

Nav, welcome copy, mail form, contact address and picture credits
preserved. 44px touch targets on phones, visible focus rings.
This commit is contained in:
Oliver committed 2026-10-23 14:41:37 +00:00
1 parent 94eb541ac3
commit 251bec598b
3 files changed
+729 -912

No files matched your search

+42 -114
View File
@@ -1,6 +1,6 @@
/* The OC Guy — page behaviour.
Everything here degrades: without JS the page still reads, the bands still
show their titles, the tips still list, the form still shows. */
Everything degrades: without JS the page is a plain, readable
document with working links and a mail form. */
(function () {
'use strict';
@@ -9,23 +9,10 @@
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
/* ---------- signature move: the shutter on the hero photograph ---------- */
var hero = document.querySelector('.hero-line');
if (hero) {
if (reduce) {
hero.classList.add('shutter');
} else {
requestAnimationFrame(function () {
setTimeout(function () { hero.classList.add('shutter'); }, 120);
});
}
}
/* ---------- entrance for the parts of every section ---------- */
var targets = [];
document.querySelectorAll('.intro .statement, .open h2, .open p, .map-figure figcaption, .tips h2, .tips-note, .picker, .tips-list li, .about h2, .bio, .about-facts, .ask-line, .ask-note, .band-text, .foot-invite, .foot-links').forEach(function (el) {
targets.push(el);
});
/* ---------- entrance for the blocks of each section ---------- */
var targets = document.querySelectorAll(
'.lead-caption, .box, .side-list, .section-head, .card, .feature, .news li, .ask-inner'
);
if (!reduce && 'IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
@@ -34,115 +21,56 @@
entry.target.classList.add('rv-in');
io.unobserve(entry.target);
});
}, { rootMargin: '0px 0px -12% 0px', threshold: 0.12 });
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });
targets.forEach(function (el, i) {
el.setAttribute('data-rv', String((i % 4) + 1));
Array.prototype.forEach.call(targets, function (el) {
el.setAttribute('data-rv', '1');
io.observe(el);
});
} else {
targets.forEach(function (el) { el.classList.add('rv-in'); });
Array.prototype.forEach.call(targets, function (el) { el.classList.add('rv-in'); });
}
/* ---------- switches: five claims ---------- */
document.querySelectorAll('.band-switch').forEach(function (btn) {
btn.addEventListener('click', function () {
var band = btn.closest('.band');
var on = btn.getAttribute('aria-checked') === 'true';
btn.setAttribute('aria-checked', String(!on));
band.setAttribute('data-on', String(!on));
});
});
/* ---------- the live thing: pick what the company does ---------- */
var chips = Array.prototype.slice.call(document.querySelectorAll('.chip'));
var out = document.querySelector('.picker-out');
var installOrder = ['Sales', 'CRM', 'Purchase', 'Manufacturing', 'Inventory', 'Project', 'Timesheets', 'Expenses', 'Invoicing', 'Accounting'];
function render(mods) {
if (!mods.length) {
out.textContent = 'Choose one, or several. The modules stack in the order you would install them.';
return;
}
out.innerHTML = '';
mods.forEach(function (name, i) {
var span = document.createElement('span');
span.className = 'mod';
span.textContent = name;
out.appendChild(span);
if (i < mods.length - 1) {
var arrow = document.createElement('span');
arrow.className = 'arrow';
arrow.textContent = ' → ';
out.appendChild(arrow);
}
});
var note = document.createElement('span');
note.className = 'arrow';
note.textContent = ' — that order, top to bottom.';
out.appendChild(note);
}
chips.forEach(function (chip) {
chip.setAttribute('aria-pressed', 'false');
chip.addEventListener('click', function () {
var pressed = chip.getAttribute('aria-pressed') === 'true';
chip.setAttribute('aria-pressed', String(!pressed));
var picked = chips.filter(function (c) { return c.getAttribute('aria-pressed') === 'true'; })
.reduce(function (all, c) {
c.dataset.modules.split(',').forEach(function (m) {
var name = m.trim();
if (all.indexOf(name) === -1) all.push(name);
});
return all;
}, []);
picked.sort(function (a, b) { return installOrder.indexOf(a) - installOrder.indexOf(b); });
render(picked);
});
});
/* ---------- ask: one question, by mail ---------- */
var send = document.querySelector('.ask-send');
/* ---------- the ask form: one question, by mail ---------- */
var form = document.querySelector('.ask-form');
var mail = document.getElementById('ask-mail');
if (send && mail) {
send.addEventListener('click', function () {
if (form && mail) {
form.addEventListener('submit', function (event) {
event.preventDefault();
var addr = (mail.value || '').trim();
var body = 'My question about Odoo Community:\n\n\n';
if (addr) body += '\n— reply to: ' + addr + '\n';
var href = 'mailto:o.arnold@projektbox.de'
+ '?subject=' + encodeURIComponent('Odoo Community question')
+ '&body=' + encodeURIComponent(body);
if (addr && addr.indexOf('@') === -1) {
mail.focus();
if (!addr || addr.indexOf('@') === -1) {
mail.setAttribute('aria-invalid', 'true');
mail.focus();
return;
}
mail.removeAttribute('aria-invalid');
window.location.href = href;
});
mail.addEventListener('keydown', function (e) {
if (e.key === 'Enter') send.click();
var body = 'My question about Odoo Community:\n\n\n— reply to: ' + addr + '\n';
window.location.href = 'mailto:o.arnold@projektbox.de'
+ '?subject=' + encodeURIComponent('Odoo Community question')
+ '&body=' + encodeURIComponent(body);
});
}
/* ---------- one moving picture: the world map drifts against the scroll ---------- */
var map = document.getElementById('worldmap');
if (map && !reduce) {
var ticking = false;
var update = function () {
var rect = map.getBoundingClientRect();
var vh = window.innerHeight || 800;
if (rect.bottom > -200 && rect.top < vh + 200) {
var progress = (rect.top + rect.height / 2 - vh / 2) / vh;
map.style.transform = 'translate3d(0,' + (progress * -26).toFixed(2) + 'px,0) scale(1.06)';
}
ticking = false;
};
window.addEventListener('scroll', function () {
if (!ticking) { ticking = true; requestAnimationFrame(update); }
}, { passive: true });
update();
/* ---------- which section the reader is in ---------- */
var links = Array.prototype.slice.call(document.querySelectorAll('.nav a[href^="#"]'));
var sections = links
.map(function (a) { return document.querySelector(a.getAttribute('href')); })
.filter(Boolean);
if (sections.length && 'IntersectionObserver' in window) {
var seen = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
links.forEach(function (a) {
if (a.getAttribute('href') === '#' + entry.target.id) {
a.setAttribute('aria-current', 'true');
} else {
a.removeAttribute('aria-current');
}
});
});
}, { rootMargin: '-45% 0px -50% 0px' });
sections.forEach(function (s) { seen.observe(s); });
}
})();