diff --git a/app/templates/base.html b/app/templates/base.html
index 97028ab..9bbb46f 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -154,6 +154,40 @@
try { localStorage.setItem('cassandra.theme', newTheme); } catch (e) {}
};
+ // Static-label i18n dictionary. AI-generated content is re-fetched via
+ // HTMX (server-side translation), but plain UI labels are baked into
+ // the HTML at render time. This dict + applyI18n() below let the
+ // language toggle swap labels live without a page refresh.
+ // Convention: … (sets textContent),
+ // (sets .placeholder).
+ // First-render correctness is handled by the template's
+ // {% if user_lang == 'it' %} block — applyI18n only kicks in on
+ // subsequent toggle events.
+ window.CASSANDRA_I18N = {
+ 'chat.title': { en: 'Ask Cassandra',
+ it: 'Chiedi a Cassandra' },
+ 'chat.hint': { en: 'grounded on the latest log + live data',
+ it: "basato sull'ultimo log + dati in tempo reale" },
+ 'chat.lede': { en: "Ask about today's analysis. The model sees the latest strategic log, live market readings across all groups, and the last 24h of thesis-filtered headlines. Refresh wipes this conversation.",
+ it: "Fai domande sull'analisi di oggi. Il modello vede l'ultimo log strategico, le quotazioni di mercato in tempo reale per tutti i gruppi e le ultime 24h di titoli filtrati per tesi. Un refresh della pagina cancella questa conversazione." },
+ 'chat.placeholder': { en: 'e.g. why is the defence sleeve flat through Hormuz?',
+ it: 'es. perché il comparto difesa è piatto nonostante Hormuz?' },
+ 'chat.send': { en: 'Send',
+ it: 'Invia' },
+ };
+ window.cassandraApplyI18n = function (lang) {
+ document.querySelectorAll('[data-i18n]').forEach(function (el) {
+ var key = el.getAttribute('data-i18n');
+ var entry = window.CASSANDRA_I18N[key];
+ if (entry && entry[lang] != null) el.textContent = entry[lang];
+ });
+ document.querySelectorAll('[data-i18n-placeholder]').forEach(function (el) {
+ var key = el.getAttribute('data-i18n-placeholder');
+ var entry = window.CASSANDRA_I18N[key];
+ if (entry && entry[lang] != null) el.placeholder = entry[lang];
+ });
+ };
+
window.cassandraSetLang = async function (newLang) {
var pill = document.getElementById('lang-toggle');
if (!pill) return;
@@ -170,6 +204,8 @@
body: JSON.stringify({lang: newLang}),
});
if (!r.ok) throw new Error('HTTP ' + r.status);
+ // Swap any static UI labels that have i18n bindings.
+ window.cassandraApplyI18n(newLang);
// Trigger HTMX-driven panels to re-fetch in the new language.
// Same shape as cassandraSetTone — every panel that listens to
// tone-changed also listens to lang-changed.
diff --git a/app/templates/log.html b/app/templates/log.html
index 8370050..cadc3f1 100644
--- a/app/templates/log.html
+++ b/app/templates/log.html
@@ -33,28 +33,18 @@
{% if paid %}
{% else %}