(function injectScreensCSS() {
if (document.getElementById('tecto-screens-css')) return;
const css = `
/* App chrome */
.tk-app { height: 100%; display: flex; flex-direction: column; background: var(--bg); color: var(--ink); overflow: hidden; }
.tk-body { flex: 1; min-height: 0; display: flex; overflow: hidden; }
.tk-main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.tk-toasts { position: fixed; right: 18px; bottom: 18px; z-index: 300; display: flex; flex-direction: column; gap: 10px; }
/* Auth screens */
.tk-auth { height: 100%; display: flex; align-items: center; justify-content: center; background: var(--bg);
background-image: radial-gradient(circle at 1px 1px, var(--border) 1px, transparent 0); background-size: 22px 22px; }
.tk-auth__card { width: 380px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl);
box-shadow: var(--shadow-xl); padding: 32px 32px 26px; }
.tk-auth__brand { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 24px; }
.tk-auth__mark { width: 46px; height: 46px; }
.tk-auth__t { font-family: var(--font-serif); font-size: 24px; font-weight: 600; letter-spacing: -0.02em; color: var(--ink-strong); }
.tk-auth__sub { font-family: var(--font-mono); font-size: 11px; color: var(--ink-muted); letter-spacing: 0.04em; margin-top: -4px; }
.tk-auth__fields { display: flex; flex-direction: column; gap: 12px; }
.tk-auth__foot { margin-top: 14px; text-align: center; font-family: var(--font-sans); font-size: 12px; color: var(--ink-muted); }
.tk-auth__link { color: var(--accent); cursor: pointer; text-decoration: underline; }
.tk-auth__err { font-family: var(--font-sans); font-size: 12px; color: var(--danger-fg); background: var(--danger-bg);
border: 1px solid var(--danger); border-radius: var(--radius-sm); padding: 7px 10px; }
/* TopBar avatar menu */
.tk-topbar { display: flex; align-items: center; gap: 10px; height: var(--topbar-h); padding: 0 12px; flex: none;
background: var(--surface); border-bottom: 1px solid var(--border); }
.tk-crumb { display: flex; align-items: center; gap: 7px; font-family: var(--font-sans); font-size: var(--text-sm); color: var(--ink-muted); }
.tk-crumb b { color: var(--ink-strong); font-weight: 600; font-family: var(--font-mono); font-size: var(--text-sm); }
.tk-topbar__spacer { flex: 1; }
.tk-topbar__grp { display: flex; align-items: center; gap: 8px; }
.tk-avatar-btn { width: 28px; height: 28px; border-radius: 50%; background: var(--accent); color: var(--accent-on);
display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-sans); font-size: 11px; font-weight: 700;
flex: none; border: none; cursor: pointer; position: relative; }
.tk-avatar-menu { position: absolute; top: calc(100% + 8px); right: 0; background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); min-width: 180px; padding: 6px; z-index: 200; }
.tk-avatar-menu__item { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: var(--radius-md);
font-family: var(--font-sans); font-size: var(--text-sm); color: var(--ink); cursor: pointer; border: none; background: none; width: 100%; }
.tk-avatar-menu__item:hover { background: var(--surface-hover); }
.tk-avatar-menu__item--danger { color: var(--danger-fg); }
.tk-avatar-menu__sep { height: 1px; background: var(--border); margin: 4px 0; }
.tk-avatar-menu__user { padding: 8px 10px 6px; }
.tk-avatar-menu__user b { display: block; font-family: var(--font-sans); font-size: var(--text-sm); font-weight: 600; color: var(--ink-strong); }
.tk-avatar-menu__user span { font-family: var(--font-mono); font-size: 11px; color: var(--ink-muted); }
/* Dynamic generator */
.tk-dyn { flex: 1; min-height: 0; display: flex; }
.tk-dyn__form { width: 432px; flex: none; min-height: 0; overflow: auto; background: var(--surface); border-right: 1px solid var(--border); }
.tk-dyn__form-in { padding: 20px 22px 60px; display: flex; flex-direction: column; gap: 14px; }
.tk-dyn__head { display: flex; align-items: center; gap: 10px; }
.tk-dyn__head h2 { font-family: var(--font-serif); font-size: 20px; font-weight: 600; letter-spacing: -0.01em; color: var(--ink-strong); margin: 0; }
.tk-dyn__sec { font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-subtle); margin: 6px 0 -4px; }
.tk-dyn__grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.tk-dyn__preview { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: var(--bg-subtle); }
.tk-dyn__bar { display: flex; align-items: center; gap: 8px; height: 44px; padding: 0 14px; flex: none; background: var(--surface); border-bottom: 1px solid var(--border); }
.tk-dyn__bar b { font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); }
.tk-dyn__sp { flex: 1; }
.tk-dyn__stage { flex: 1; min-height: 0; overflow: auto; padding: 28px; display: flex; justify-content: center; align-items: flex-start; position: relative; }
.tk-dyn__page { width: 480px; background: var(--paper); box-shadow: var(--shadow-lg); border-radius: 2px; min-height: 620px; }
.tk-dyn__empty { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; gap: 14px; color: var(--ink-subtle); font-family: var(--font-mono); font-size: 13px; }
.tk-dyn__overlay { position: absolute; inset: 0; display: flex; flex-direction: column; gap: 13px; align-items: center; justify-content: center; background: color-mix(in srgb, var(--bg-subtle) 78%, transparent); backdrop-filter: blur(1.5px); }
.tk-dyn__overlay span { font-family: var(--font-mono); font-size: 13px; color: var(--ink-muted); }
.tk-items-grid { display: grid; grid-template-columns: 1fr 60px 90px; gap: 7px; align-items: center; }
.tk-items-hdr { font-family: var(--font-mono); font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-subtle); padding: 0 2px; margin-bottom: -2px; }
.tk-totals { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; background: var(--surface-sunken); border: 1px solid var(--border); border-radius: var(--radius-md); font-family: var(--font-mono); font-size: 12px; }
.tk-totals .r { display: flex; justify-content: space-between; }
.tk-totals .t { font-weight: 600; font-size: 14px; padding-top: 6px; margin-top: 2px; border-top: 1px solid var(--border-strong); }
/* Free editor */
.tk-free { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.tk-free__bar { display: flex; align-items: center; gap: 8px; height: 44px; padding: 0 14px; flex: none; background: var(--surface); border-bottom: 1px solid var(--border); }
.tk-free__body { flex: 1; min-height: 0; display: flex; }
.tk-free__editor { flex: 1; min-height: 0; display: flex; flex-direction: column; border-right: 1px solid var(--border); }
.tk-free__ta { flex: 1; width: 100%; resize: none; border: none; outline: none; padding: 16px 18px; font-family: var(--font-mono); font-size: 13px; line-height: 1.6; background: var(--code-bg); color: var(--code-text); tab-size: 2; }
.tk-free__preview { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: var(--bg-subtle); position: relative; }
/* Template editor with inspector */
.tk-tpl-edit { flex: 1; min-height: 0; display: flex; }
.tk-tpl-insp { width: 240px; flex: none; min-height: 0; overflow: auto; background: var(--surface); border-right: 1px solid var(--border); padding: 16px; display: flex; flex-direction: column; gap: 14px; }
.tk-tpl-insp h3 { font-family: var(--font-serif); font-size: 16px; font-weight: 600; color: var(--ink-strong); margin: 0; }
.tk-tpl-insp__sec { font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink-subtle); margin-top: 4px; }
.tk-var-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 8px; background: var(--surface-sunken); border: 1px solid var(--border); border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: 11px; color: var(--code-cmd); cursor: pointer; margin: 2px; }
.tk-var-chip:hover { background: var(--accent-subtle-bg); border-color: var(--border-accent); }
/* Assets screen */
.tk-assets-real { flex: 1; overflow: auto; background: var(--bg); }
.tk-assets-real__in { max-width: 960px; margin: 0 auto; padding: 30px 36px 70px; }
.tk-asset-real { display: flex; align-items: center; gap: 12px; padding: 10px 13px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); }
.tk-asset-real__thumb { width: 48px; height: 48px; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface-sunken); display: flex; align-items: center; justify-content: center; flex: none; overflow: hidden; }
.tk-asset-real__thumb img { width: 100%; height: 100%; object-fit: cover; }
.tk-asset-real__n { font-family: var(--font-mono); font-size: 12px; color: var(--ink); flex: 1; }
.tk-asset-real__m { font-family: var(--font-sans); font-size: 11px; color: var(--ink-subtle); }
.tk-asset-real__acts { display: flex; align-items: center; gap: 4px; }
/* Profile screen */
.tk-profile { flex: 1; overflow: auto; background: var(--bg); }
.tk-profile__in { max-width: 560px; margin: 0 auto; padding: 36px 36px 80px; display: flex; flex-direction: column; gap: 22px; }
.tk-profile__card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl); padding: 26px; box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: 18px; }
.tk-profile__avatar { width: 64px; height: 64px; border-radius: 50%; background: var(--accent); color: var(--accent-on); display: flex; align-items: center; justify-content: center; font-family: var(--font-serif); font-size: 26px; font-weight: 700; }
.tk-profile__name { font-family: var(--font-serif); font-size: 22px; font-weight: 600; color: var(--ink-strong); }
.tk-profile__email { font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); }
.tk-profile__fields { display: flex; flex-direction: column; gap: 12px; }
/* Settings screen */
.tk-settings { flex: 1; overflow: auto; background: var(--bg); }
.tk-settings__in { max-width: 600px; margin: 0 auto; padding: 32px 36px 80px; display: flex; flex-direction: column; gap: 20px; }
.tk-set-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px 22px; box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: 14px; }
.tk-set-card h3 { font-family: var(--font-serif); font-size: 16px; font-weight: 600; color: var(--ink-strong); margin: 0 0 2px; }
.tk-set-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.tk-set-row span { font-family: var(--font-sans); font-size: var(--text-sm); color: var(--ink-muted); }
/* Split pane divider */
.tk-split-divider { width: 5px; flex-shrink: 0; cursor: col-resize; background: var(--border); transition: background 150ms; user-select: none; }
.tk-split-divider:hover, .tk-split-divider:active { background: var(--accent); }
/* Plantillas categories */
.tk-cat-head { font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-subtle); margin: 20px 0 10px; }
/* Login */
.tk-login__card { width: 360px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-xl);
box-shadow: var(--shadow-xl); padding: 30px 30px 26px; }
.tk-login__brand { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-bottom: 22px; }
.tk-login__mark { width: 46px; height: 46px; }
.tk-login__t { font-family: var(--font-serif); font-size: 24px; font-weight: 600; letter-spacing: -0.02em; color: var(--ink-strong); }
.tk-login__sub { font-family: var(--font-mono); font-size: 11px; color: var(--ink-muted); letter-spacing: 0.04em; margin-top: -6px; }
.tk-login__fields { display: flex; flex-direction: column; gap: 13px; }
.tk-login__foot { margin-top: 16px; text-align: center; font-family: var(--font-sans); font-size: 11px; color: var(--ink-subtle); }
/* Modal */
.tk-modal-ov { position:fixed;inset:0;background:rgba(0,0,0,.38);z-index:400;display:flex;align-items:center;justify-content:center; }
.tk-modal { background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-xl);box-shadow:var(--shadow-xl);padding:26px;width:420px;display:flex;flex-direction:column;gap:14px; }
.tk-modal__title { font-family:var(--font-serif);font-size:18px;font-weight:600;color:var(--ink-strong); }
.tk-modal__field { display:flex;flex-direction:column;gap:5px; }
.tk-modal__label { font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-muted); }
.tk-modal__input { height:36px;padding:0 10px;border-radius:var(--radius-md);border:1px solid var(--border-strong);background:var(--surface);color:var(--ink);font-family:var(--font-sans);font-size:13px;outline:none;width:100%; }
.tk-modal__input:focus { border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-ring); }
.tk-modal__actions { display:flex;justify-content:flex-end;gap:8px;margin-top:4px; }
/* Shared workspace layout */
.tk-ws { flex: 1; min-height: 0; overflow: auto; background: var(--bg); }
.tk-ws__in { max-width: 960px; margin: 0 auto; padding: 30px 36px 70px; }
.tk-ws__head { display: flex; align-items: flex-end; gap: 14px; margin-bottom: 6px; }
.tk-ws__title { font-family: var(--font-serif); font-size: 28px; font-weight: 600; letter-spacing: -0.02em; color: var(--ink-strong); margin: 0; }
.tk-ws__sub { font-family: var(--font-sans); font-size: var(--text-sm); color: var(--ink-muted); margin: 3px 0 0; }
.tk-ws__sp { flex: 1; }
.tk-ws__sec { font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-subtle); margin: 28px 0 13px; }
`;
const el = document.createElement('style');
el.id = 'tecto-screens-css';
el.textContent = css;
document.head.appendChild(el);
})();
// ── NewDocModal ───────────────────────────────────────────────────────────────
const NEW_CLIENT_SENTINEL = '__new__';
function NewDocModal({ templateName, clients, onClose, onCreate }) {
const [docName, setDocName] = React.useState('');
const [clientId, setClientId] = React.useState(clients.length > 0 ? clients[0].id : NEW_CLIENT_SENTINEL);
const [newClientName, setNewClientName] = React.useState('');
const [saving, setSaving] = React.useState(false);
const { Button } = window.TectoDS;
const isNew = clientId === NEW_CLIENT_SENTINEL;
const canSubmit = docName.trim() && (!isNew || newClientName.trim());
const handleCreate = async () => {
if (!canSubmit) return;
setSaving(true);
try {
await onCreate({
name: docName.trim(),
clientId: isNew ? null : clientId,
newClientName: isNew ? newClientName.trim() : null,
});
} finally {
setSaving(false);
}
};
return (
e.stopPropagation()}>
Nuevo documento — {templateName}
setDocName(e.target.value)} autoFocus
onKeyDown={e => { if (e.key === 'Enter' && canSubmit) handleCreate(); if (e.key === 'Escape') onClose(); }} />
{isNew && (
setNewClientName(e.target.value)}
onKeyDown={e => { if (e.key === 'Enter' && canSubmit) handleCreate(); if (e.key === 'Escape') onClose(); }} />
)}
);
}
// ── ItemsTable ────────────────────────────────────────────────────────────────
const ITEM_KEYS = new Set(['item_1_desc','item_1_hrs','item_1_total','item_2_desc','item_2_hrs','item_2_total',
'item_3_desc','item_3_hrs','item_3_total','subtotal','itbis','total','total_doc']);
function ItemsTable({ data, setData }) {
const { Input } = window.TectoDS;
const set = (n, key, val) => {
const updated = { ...data, [`item_${n}_${key}`]: val };
if (key === 'total') {
const nums = [1,2,3].map(i => parseFloat((updated[`item_${i}_total`]||'0').replace(/,/g,'')) || 0);
const sub = nums.reduce((a,b) => a+b, 0);
const itbis = sub * 0.18;
const fmt = (x) => x.toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
updated.subtotal = fmt(sub);
updated.itbis = fmt(itbis);
updated.total = fmt(sub + itbis);
}
setData(updated);
};
const moneda = data.moneda || 'USD';
return (
DescripciónHrs/UTotal
{[1,2,3].map(n => (
set(n,'desc',e.target.value)} />
set(n,'hrs',e.target.value)} />
set(n,'total',e.target.value)} />
))}
{[['Subtotal', data.subtotal],['ITBIS 18%', data.itbis],['Total', data.total]].map(([k,v]) => (
{k}
{v ? `${moneda} ${v}` : '—'}
))}
);
}
// ── FieldInput ────────────────────────────────────────────────────────────────
function FieldInput({ field, value, onChange }) {
const { Input, Select } = window.TectoDS;
if (field.type === 'textarea') return (
);
if (field.type === 'select') return (