class JTFLogin extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
OPERATOR AUTHENTICATION
Status: SECURE | Version: 1.0.0
`;
this._runSequence();
}
_type(el, text, delay) {
return new Promise(resolve => {
let i = 0;
const t = setInterval(() => {
el.textContent = text.slice(0, ++i);
if (i >= text.length) { clearInterval(t); resolve(); }
}, delay);
});
}
_wait(ms) { return new Promise(r => setTimeout(r, ms)); }
_setStatus(msg, cls = '') {
const s = this.shadowRoot.getElementById('status');
s.textContent = msg;
s.className = 'status ' + cls;
}
async _runSequence() {
const root = this.shadowRoot;
const fUser = root.getElementById('f-user');
const fPass = root.getElementById('f-pass');
const btn = root.getElementById('btn');
await this._wait(700);
this._setStatus('> INITIALISING ENCRYPTED CHANNEL...', 'active');
await this._wait(1200);
this._setStatus('> BIOMETRIC HANDSHAKE CONFIRMED', 'active');
await this._wait(900);
this._setStatus('> ENTER CREDENTIALS', 'active');
await this._wait(600);
await this._type(fUser, 'OPERATOR_291', 75);
fUser.classList.add('filled');
await this._wait(300);
await this._type(fPass, '● ● ● ● ● ● ● ●', 55);
fPass.classList.add('filled');
await this._wait(400);
this._setStatus('> AUTHENTICATING — STANDBY...', 'active');
await this._wait(300);
btn.disabled = false;
btn.classList.add('flash');
await this._wait(1500);
btn.classList.remove('flash');
this._setStatus('> ACCESS GRANTED — IDENTITY VERIFIED ✓', 'success');
fUser.classList.remove('filled');
fPass.classList.remove('filled');
fUser.classList.add('locked');
fPass.classList.add('locked');
btn.textContent = 'PROCEED ▶';
btn.classList.add('proceed');
btn.addEventListener('click', () => {
document.getElementById('main-nav')?.scrollIntoView({ behavior: 'smooth' });
}, { once: true });
}
}
customElements.define('jtf-login', JTFLogin);