feat(5.16+5.17): tipografie/antet branded + tipuri cont, planuri si enforcement

PRD 5.16 — propagare design finalizata (system font stack, fara IBM Plex self-hostat):
- US-001/002/008: tokeni --font-ui/--font-mono (system stack) + scala --fs-*; zero
  @font-face si zero /static/fonts/; landing aliniat la acelasi stack
- US-003: RAR online = dot compact in antet + meniu burger; banda rosie DOAR pe blocat
  (invariant zero-silent-failures pastrat)
- US-010: antet "ROMFAST AUTOPASS" + nume service + /login brandeit 2 coloane + badge plan;
  meniu burger cu separatoare; gate strict pe is_authenticated
- US-011: selector tema pill icon+eticheta (reuse THEMES)
- US-004/005/006/007: bug-fix editor prestatii (picker cod+denumire, add_extra in mod
  operatii, cod ales se salveaza fara "+", Renunta inchide via closest)
- US-012/013: landing Autentificare->/login; wizard import colapsat + 4 pasi pe tokeni
- fix VERIFY E2E: contoare duplicate pe 390px (inline display:flex batea @media) -> CSS + test-lock

PRD 5.17 — tipuri de cont + trial Pro 30z + enforcement DUR:
- US-001/002/008: accounts.tier + trial_until (migrare aditiva defensiva); app/plans.py
  sursa unica (PLANS, FREE_MONTHLY_LIMIT=60, effective_tier(now injectabil), monthly_usage,
  CONSUMED_STATUSES); create_account trial Pro 30z; CLI set-tier (protejat id=1, audit)
- US-003/004/005: enforce volum 60/luna INAINTE de build_key pe ambele canale
  (PLAN_LIMITA_LUNARA, 3 niveluri + log_event); gate API Pro+ (PLAN_FARA_API 403 actionabil);
  valideaza/nomenclator raman permise; downgrade lazy; flag AUTOPASS_ENFORCE_PLANS (kill-switch)
- US-006: badge plan antet + linie burger + consum N/60 + warn>=80% + 6 stari + copy RO
  pluralizat + banner one-time trial->Gratuit + pagina Cont

Regresie: 1380 passed, 0 failed, 1 deselected (live). E2E browser pe 390/1280 confirmat.
Backend trimitere (worker/masina stari/idempotenta/contract RAR) NEATINS. Lucrul 5.18
(corpus kNN) ramane separat, necomis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-06-29 06:01:05 +00:00
parent 9eccb9f6fa
commit c9f9a1ca0e
37 changed files with 3433 additions and 449 deletions

View File

@@ -26,6 +26,7 @@ from fastapi.templating import Jinja2Templates
from .. import __version__
from .. import errors as _errors
from ..auth import rotate_api_key
from ..plans import effective_tier as _eff_tier, monthly_usage as _monthly_usage, PLANS as _PLANS
from ..payload_view import prezentare_din_payload
from ..web.csrf import get_csrf_token, verify_csrf
from .labels import (
@@ -374,7 +375,7 @@ def _render_panel_cont(request: Request, conn, account_id: int) -> str:
).fetchone()
are_creds = bool(row and row["rar_creds_enc"])
account_meta = _fetch_account_meta(conn, acct)
return templates.get_template("_cont.html").render({
cont_ctx = {
"request": request,
"csrf_token": get_csrf_token(request),
"api_key": None,
@@ -385,7 +386,10 @@ def _render_panel_cont(request: Request, conn, account_id: int) -> str:
"account_meta": account_meta,
"date_firma_mesaj": None,
"date_firma_eroare": None,
})
}
# US-006 (5.17): context plan pentru sectiunea Plan din _cont.html.
cont_ctx.update(_plan_ctx(conn, account_id))
return templates.get_template("_cont.html").render(cont_ctx)
def _render_panel_nomenclator(request: Request, conn) -> str:
@@ -531,6 +535,139 @@ def _render_panel_for_tab(request: Request, conn, account_id: int, tab: str, sta
return _render_panel_acasa(request)
# Etichete tier pentru badge in antet (US-010 PRD 5.16).
_TIER_LABELS: dict[str, str] = {
"free": "Gratuit",
"standard": "Standard",
"pro": "Pro",
"premium": "Premium",
}
def _plan_ctx(conn, account_id: int, now: datetime | None = None) -> dict:
"""Context afisaj plan (6 stari US-006 PRD 5.17) pentru _status.html, _cont.html si burger.
Returneaza:
plan_linie — linie completa cu copy RO (cele 6 stari)
plan_warn — True la >=80% consum sau limita atinsa (culoare + text)
plan_limita_atinsa — True la 100% consum (--err in loc de --warn)
trial_expirat_recent — True daca trial_until era setat si a expirat (banner one-time)
usage_lunar — numar prestatii acceptate in coada luna curenta
monthly_limit_val — limita lunara (60 pt free, None pt nelimitat)
effective_tier_name — tier-ul efectiv ('free','standard','pro','premium')
"""
if now is None:
now = datetime.now(timezone.utc)
acct = account_or_default(account_id)
row = conn.execute(
"SELECT tier, trial_until FROM accounts WHERE id=?", (acct,)
).fetchone()
tier_base = (row["tier"] if row else None) or "free"
trial_until_str = (row["trial_until"] if row else None)
eff = _eff_tier(row, now) if row else "free"
monthly_limit = _PLANS.get(eff, _PLANS["free"]).get("monthly_limit")
usage = _monthly_usage(conn, acct, now)
# Calcul zile ramase din trial activ
trial_ultima_zi = False
trial_days: int | None = None
if trial_until_str and eff == "pro" and tier_base == "free":
try:
tu = datetime.fromisoformat(trial_until_str.strip().replace(" ", "T"))
if tu.tzinfo is None:
tu = tu.replace(tzinfo=timezone.utc)
now_cmp = now if now.tzinfo else now.replace(tzinfo=timezone.utc)
delta = tu - now_cmp
trial_days = delta.days # 0 = < 1 zi ramasa (azi), 1 = < 2 zile, etc.
trial_ultima_zi = (trial_days <= 0)
except (ValueError, AttributeError, TypeError):
pass
# Construieste plan_linie si stari aferente (cele 6 stari din PRD)
warn_aproape = False
plan_limita_atinsa = False
trial_expirat_recent = False
if eff == "pro" and tier_base == "free" and trial_until_str:
# Trial Pro activ
if trial_ultima_zi:
plan_linie = "Plan: Pro · trial expira azi"
else:
n = trial_days or 0
z = "zi" if n == 1 else "zile"
plan_linie = f"Plan: Pro · trial {n} {z} ramase"
elif eff == "free":
# Free — cu sau fara trial expirat recent
if trial_until_str:
trial_expirat_recent = True
if monthly_limit is not None:
if usage >= monthly_limit:
plan_linie = f"Plan: Gratuit · {usage}/{monthly_limit} — limita atinsa"
warn_aproape = True
plan_limita_atinsa = True
elif monthly_limit > 0 and usage >= int(monthly_limit * 0.8):
plan_linie = f"Plan: Gratuit · {usage}/{monthly_limit} — aproape de limita"
warn_aproape = True
else:
plan_linie = f"Plan: Gratuit · {usage}/{monthly_limit} luna asta"
else:
plan_linie = "Plan: Gratuit"
else:
# Platit (tier de baza != free, ex. standard/pro/premium alocat de admin)
label = _PLANS.get(eff, {}).get("label", eff.capitalize())
plan_linie = f"Plan: {label}"
return {
"plan_linie": plan_linie,
"plan_warn": warn_aproape,
"plan_limita_atinsa": plan_limita_atinsa,
"trial_expirat_recent": trial_expirat_recent,
"usage_lunar": usage,
"monthly_limit_val": monthly_limit,
"effective_tier_name": eff,
}
def _layout_header_ctx(conn, account_id: int) -> dict:
"""Context suplimentar pentru antetul branduit (US-010/003, PRD 5.16).
Citeste account_name, tier si starea de sanatate RAR pentru a popula:
- account_name: numele service-ului, afisat sub titlu cand logat
- tier_label: eticheta planului (Gratuit/Standard/Pro/Premium)
- sanatate_ok: True daca worker viu si RAR ok (dot verde in antet)
- last_login: data/ora ultimei autentificari RAR (format romanesc)
- plan_linie + plan_warn + ...: context plan US-006 (5.17) pentru burger
Apelat aditiv din dashboard() fara a atinge alti handlere.
"""
row = conn.execute(
"SELECT name, tier FROM accounts WHERE id=?", (account_id,)
).fetchone()
account_name = (row["name"] if row else None) or ""
tier = (row["tier"] if row else "free") or "free"
tier_label = _TIER_LABELS.get(tier, "Gratuit")
hb = read_heartbeat(conn)
worker_alive = _worker_alive(hb)
rar_state = _rar_state(hb, worker_alive)
rar_ok = rar_state == "ok"
sanatate_ok = worker_alive and rar_ok
ctx = {
"account_name": account_name,
"tier_label": tier_label,
"sanatate_ok": sanatate_ok,
"last_login": format_data_rar(hb["last_rar_login_ok"] if hb else None),
}
# US-006 (5.17): context plan pentru linia detaliata din meniul burger.
ctx.update(_plan_ctx(conn, account_id))
return ctx
@router.get("/", response_class=HTMLResponse)
def dashboard(request: Request, tab: str = "acasa", status: str | None = None) -> HTMLResponse:
"""Dashboard principal cu tab-uri.
@@ -578,6 +715,9 @@ def dashboard(request: Request, tab: str = "acasa", status: str | None = None) -
"is_admin": is_account_admin(conn, account_id),
"csrf_token": get_csrf_token(request),
}
# US-010/003 (PRD 5.16): context antet (account_name, tier, sanatate RAR).
# Adaugat aditiv, fara a atinge handlerele altora.
ctx.update(_layout_header_ctx(conn, account_id))
return templates.TemplateResponse("dashboard.html", ctx)
finally:
conn.close()
@@ -749,7 +889,7 @@ def _build_status_ctx(request: Request, conn, account_id: int, *, oob: bool = Fa
else:
sanatate_text = "Declaratiile curg normal"
return {
status_ctx = {
"request": request,
"worker_lbl": worker_lbl,
"rar_lbl": rar_lbl,
@@ -771,6 +911,9 @@ def _build_status_ctx(request: Request, conn, account_id: int, *, oob: bool = Fa
"mapari_badge": counts.get("needs_mapping", 0),
"oob": oob,
}
# US-006 (5.17): context plan pentru linia de consum/trial in _status.html.
status_ctx.update(_plan_ctx(conn, account_id))
return status_ctx
@router.get("/_fragments/status", response_class=HTMLResponse)
@@ -1363,6 +1506,18 @@ async def post_corectie_trimitere(request: Request, submission_id: int) -> HTMLR
c.strip().upper() if isinstance(c, str) else ""
for c in codes_raw
]
# US-006 (5.16): codul ales in picker dar ne-aprobat prin '+' se aplica implicit la salvare.
# Picker flat (chips_add_cod_flat): cod ales dar neselectat ca chip → adaugat la sfarsit.
# Picker per-operatie (chips_add_cod_{i}): cod ales pe pozitia i dar ne-aprobat → adaugat pozitional.
# Ambele validate fata de nomenclator in bucla de validare de mai jos (invariant ORA-12899).
_flat_picker = str(form.get("chips_add_cod_flat") or "").strip().upper()
if _flat_picker and _flat_picker not in codes_positional:
codes_positional.append(_flat_picker)
for _i in range(len(codes_positional)):
if not codes_positional[_i]:
_op_val = str(form.get(f"chips_add_cod_{_i}") or "").strip().upper()
if _op_val:
codes_positional[_i] = _op_val
# Verifica daca cel putin un cod non-gol a fost trimis
codes_nonempty = [c for c in codes_positional if c]
if codes_nonempty:
@@ -1923,6 +2078,7 @@ async def post_form_chips(request: Request) -> HTMLResponse:
})
action = str(form.get("chips_action") or "").strip()
chips_extra_error = False # T-C1/T-E4 (5.16): semnal pentru add_extra esuat
conn = get_connection()
try:
@@ -1950,6 +2106,28 @@ async def post_form_chips(request: Request) -> HTMLResponse:
if exists:
chips.append({"cod_prestatie": add_cod_flat, "cod_op_service": "", "denumire": ""})
elif action == "add_extra":
# US-005 (5.16): Adauga cod RAR liber (extra, fara op_service) in modul operatii.
# Refoloseste `chips_add_cod_flat` (acelasi select; dedup per-item E4 pastrat).
# T-C1/T-E4: select gol sau cod invalid → chips_extra_error = True (semnal vizibil).
add_cod_extra = str(form.get("chips_add_cod_flat") or "").strip().upper()
if add_cod_extra:
exists = conn.execute(
"SELECT 1 FROM nomenclator_rar WHERE cod_prestatie=?", (add_cod_extra,)
).fetchone()
if exists:
# Dedup per-item (E4): nu adauga un chip extra identic deja existent
existing_pairs = {
(c.get("cod_op_service", ""), c.get("cod_prestatie", ""))
for c in chips
}
if ("", add_cod_extra) not in existing_pairs:
chips.append({"cod_prestatie": add_cod_extra, "cod_op_service": "", "denumire": ""})
else:
chips_extra_error = True # cod necunoscut in nomenclator
else:
chips_extra_error = True # select gol
elif action == "remove":
# Sterge codul de la indexul dat (lasa op_service intact -> operatie ramane nemapata)
try:
@@ -1983,6 +2161,7 @@ async def post_form_chips(request: Request) -> HTMLResponse:
"has_r_odo": has_r_odo,
"form_chips_url": "/form-chips",
"chips_section_id": "chips-section",
"chips_extra_error": chips_extra_error, # T-C1/T-E4 (5.16)
})
@@ -3525,6 +3704,46 @@ async def web_confirma_import(
n_total_ok = len(to_enqueue)
# T3 (PRD 5.17): enforce volum plan — INAINTE de enqueue (invariant idempotenta).
# Decizie #21: respingere TOTALA a lotului (nu enqueue partial tacut). Canal web.
from ..config import get_settings as _get_settings_plan
from ..plans import PLANS as _PLANS, effective_tier as _effective_tier, monthly_usage as _monthly_usage
_plan_settings = _get_settings_plan()
if _plan_settings.enforce_plans and n_total_ok > 0:
from datetime import datetime, timezone as _tz
_acct_row = conn.execute(
"SELECT tier, trial_until FROM accounts WHERE id=?", (acct,)
).fetchone()
_now_plan = datetime.now(_tz.utc)
_et = _effective_tier(_acct_row, _now_plan)
_plan_limit = _PLANS[_et].get("monthly_limit")
if _plan_limit is not None:
_usage = _monthly_usage(conn, acct, _now_plan)
if _usage + n_total_ok > _plan_limit:
_remaining = max(0, _plan_limit - _usage)
log_event(
"plan_limita_lunara_atinsa",
account_id=acct,
nivel="WARNING",
mesaj=f"Import web de {n_total_ok} respins (usage={_usage}, limita={_plan_limit})",
context={
"n_to_enqueue": n_total_ok, "usage": _usage,
"plan_limit": _plan_limit, "tier": _et,
},
conn=conn,
)
_err_msg = (
f"Ai atins limita planului Gratuit: {_usage}/{_plan_limit} prezentari luna aceasta."
f" Mai poti trimite {_remaining} luna aceasta."
f" Treci pe Standard sau Pro, sau asteapta luna viitoare."
)
_prev_result = _web_compute_preview(conn, import_id, account_id)
if isinstance(_prev_result, str):
return templates.TemplateResponse("_upload.html", _ctx(request, error=_err_msg))
return templates.TemplateResponse("_preview_import.html", _ctx(
request, import_id=import_id, message=_err_msg, error=True, **_prev_result
))
# Gate HARD: n_confirmat trebuie sa fie exact egal cu numarul de randuri de trimis
if n_confirmat != n_total_ok:
result = _web_compute_preview(conn, import_id, account_id)

View File

@@ -21,7 +21,7 @@
In timpul fluxului (mapcoloane/preview), HTMX face swap pe #import-section (descendentul
intern) → <details> ramane neatins → containerul ramane deschis intre pasi. === #}
<details id="import-details"{% if not are_trimiteri %} open{% endif %}>
<summary>Importa un fisier</summary>
<summary>+ Importa fisier (XLSX / CSV)</summary>
{% include '_upload.html' %}
</details>

View File

@@ -117,6 +117,60 @@
{% endif %}
{% endfor %}
{# ===== US-005 (5.16): Chips extra + picker '+ Adauga alta operatie / cod RAR' in mod operatii ===== #}
{# Chips extra: cod_op_service gol, cod_prestatie setat — afisate flat cu × (reuse remove_flat) #}
<div class="chips" role="group" aria-label="Coduri RAR suplimentare" style="margin-top:4px;">
{% for chip in _chips %}
{% if not chip.cod_op_service and chip.cod_prestatie %}
{% set _is_warn_extra = chip.cod_prestatie in ('R-ODO', 'I-ODO') %}
<span class="chip {% if _is_warn_extra %}chip-warn{% endif %}"
aria-label="Cod RAR suplimentar {{ chip.cod_prestatie }}">
{{ chip.cod_prestatie }}
<button type="button" class="chip-del"
hx-post="{{ _chips_url }}"
hx-include="closest form"
hx-target="#{{ _sec_id }}"
hx-swap="outerHTML"
hx-vals='{"chips_action":"remove_flat","chips_remove_cod":"{{ chip.cod_prestatie }}"}'
aria-label="Sterge codul suplimentar {{ chip.cod_prestatie }}">&times;</button>
</span>
{% endif %}
{% endfor %}
</div>
{% if nomenclator_rar %}
<span style="display:inline-flex;align-items:center;gap:4px;margin-top:4px;">
<select name="chips_add_cod_flat"
aria-label="Adauga cod RAR suplimentar"
style="min-width:160px;font-size:11px;height:26px;border:1px dashed color-mix(in srgb,var(--accent) 55%,var(--line));border-radius:5px;background:transparent;color:var(--accent);">
<option value="">+ Adauga alta operatie / cod RAR</option>
{% for n in nomenclator_rar %}
<option value="{{ n.cod_prestatie }}">{{ n.cod_prestatie }} — {{ n.nume_prestatie }}</option>
{% endfor %}
</select>
<button type="button"
class="add-code"
hx-post="{{ _chips_url }}"
hx-include="closest form"
hx-target="#{{ _sec_id }}"
hx-swap="outerHTML"
hx-vals='{"chips_action":"add_extra"}'
aria-label="Adauga cod RAR suplimentar la trimitere">
+
</button>
</span>
{% else %}
{# T-D1/T-E5 (5.16): empty state in mod operatii cand nomenclatorul lipseste #}
<div class="chips-nom-gol" style="font-size:11px;color:var(--warn);padding:4px 0;margin-top:4px;">
Nomenclator indisponibil — adaugarea de coduri suplimentare nu e posibila.
</div>
{% endif %}
{% if chips_extra_error %}
{# T-C1/T-E4 (5.16): semnal vizibil cand add_extra are select gol sau cod invalid #}
<div class="chips-extra-error" style="font-size:11px;color:var(--err);padding:2px 0;" role="alert">
Selecteaza un cod RAR din lista inainte de a adauga.
</div>
{% endif %}
{% else %}
{# ===== Mod plat: lista de coduri libere (corectie pura, fara op_service) ===== #}
<div class="chips" role="group" aria-label="Coduri RAR selectate">
@@ -144,7 +198,7 @@
style="font-size:11px;height:22px;border:1px dashed color-mix(in srgb,var(--accent) 55%,var(--line));border-radius:5px;background:transparent;color:var(--accent);">
<option value="">+ cod</option>
{% for n in nomenclator_rar %}
<option value="{{ n.cod_prestatie }}">{{ n.cod_prestatie }}</option>
<option value="{{ n.cod_prestatie }}">{{ n.cod_prestatie }} — {{ n.nume_prestatie }}</option>
{% endfor %}
</select>
<button type="button"
@@ -158,6 +212,11 @@
+
</button>
</span>
{% else %}
{# T-D1/T-E5 (5.16): empty state in mod plat cand nomenclatorul lipseste #}
<div class="chips-nom-gol" style="font-size:11px;color:var(--warn);padding:4px 0;">
Nomenclator indisponibil — nu se pot adauga coduri RAR momentan.
</div>
{% endif %}
</div>
{% endif %}

View File

@@ -1,6 +1,38 @@
<div class="card" id="card-cont">
<h2 style="font-size:15px; margin:0 0 16px;">Contul meu</h2>
<!-- Sectiunea: Plan curent (US-006 PRD 5.17) -->
{% if plan_linie is defined %}
<div id="sectiune-plan" style="margin-bottom:20px; padding-bottom:20px; border-bottom:1px solid var(--line);">
<h3 style="font-size:var(--fs-sm); color:var(--muted); font-weight:500; margin:0 0 10px;
text-transform:uppercase; letter-spacing:.04em;">Plan curent</h3>
<div style="font-size:var(--fs-md); font-weight:600; margin-bottom:6px;
color:{% if plan_limita_atinsa|default(false) %}var(--err){% elif plan_warn|default(false) %}var(--warn){% else %}var(--ink){% endif %};">
{{ plan_linie }}
</div>
{% if monthly_limit_val is defined and monthly_limit_val is not none and effective_tier_name|default('') == 'free' %}
<div style="font-size:var(--fs-sm); color:var(--muted); margin-bottom:8px;">
Planul Gratuit include {{ monthly_limit_val }} prestatii/luna prin dashboard-ul web.
{% if plan_limita_atinsa|default(false) %}
Limita lunara a fost atinsa — trimiterile noi sunt blocate pana la inceputul lunii urmatoare.
{% elif plan_warn|default(false) %}
Te apropii de limita lunara.
{% endif %}
</div>
{% endif %}
<div style="font-size:var(--fs-sm); color:var(--muted); padding:8px 10px;
border:1px solid var(--line); border-radius:6px; margin-top:4px;">
Vrei sa treci pe Standard, Pro sau Premium?
Contacteaza-ne pentru alocare manuala — nu exista inca plata self-service.
<strong>Pro</strong> adauga import prin API; <strong>Standard</strong> si
<strong>Premium</strong> ridica limita de volum.
</div>
</div>
{% endif %}
<!-- Sectiunea: Date firma (US-002) -->
<div style="margin-bottom:20px; padding-bottom:20px; border-bottom:1px solid var(--line);">
<h3 style="font-size:13px; color:var(--muted); font-weight:500; margin:0 0 8px; text-transform:uppercase; letter-spacing:.04em;">Date firma</h3>

View File

@@ -4,7 +4,7 @@
{# prima_inregistrare poate veni din context (web_upload_import) sau derivat din sample_rows #}
{%- set prima_inreg = prima_inregistrare if prima_inregistrare is defined else (sample_rows[0] if sample_rows else none) -%}
<div class="card">
<h2 style="font-size:15px; margin:0 0 12px;">
<h2 style="font-size:var(--fs-md); margin:0 0 12px;">
Mapare coloane —
<span class="muted" style="font-weight:400;">{{ filename or ("import #" ~ import_id) }}</span>
</h2>
@@ -20,19 +20,19 @@
</div>
{% endif %}
<p class="muted" style="margin:0 0 12px; font-size:13px;">
<p class="muted" style="margin:0 0 12px; font-size:var(--fs-sm);">
Asociaza fiecare coloana din fisier cu campul canonic corespunzator.
Maparea se retine automat pentru fisiere cu acelasi antet.
</p>
{# Tabel orizontal preview: antet + prima inregistrare (US-003) #}
{# Tabel orizontal preview: antet + prima inregistrare (compatibilitate teste) #}
<div class="tablewrap" style="margin-bottom:16px;">
<table class="preview-antet" style="border-collapse:collapse; font-size:12px; width:100%; min-width:max-content;">
<table class="preview-antet" style="border-collapse:collapse; font-size:var(--fs-xs); width:100%; min-width:max-content;">
<thead>
<tr>
{% for col in columns %}
<th style="padding:4px 10px; text-align:left; background:var(--card); border:1px solid var(--line);
white-space:nowrap; font-weight:600; font-size:12px; color:var(--ink);">
white-space:nowrap; font-weight:600; font-size:var(--fs-xs); color:var(--ink);">
{{ col }}
</th>
{% endfor %}
@@ -44,7 +44,7 @@
{% for col in columns %}
{%- set val = prima_inreg.get(col, '') | string -%}
<td style="padding:4px 10px; border:1px solid var(--line); white-space:nowrap;
font-size:11px; color:var(--muted); max-width:160px; overflow:hidden; text-overflow:ellipsis;"
font-size:var(--fs-xs); color:var(--muted); max-width:160px; overflow:hidden; text-overflow:ellipsis;"
title="{{ val }}">
{{ val[:40] }}{% if val | length > 40 %}…{% endif %}
</td>
@@ -53,7 +53,7 @@
{% else %}
<tr>
<td colspan="{{ columns | length }}"
style="padding:6px 10px; border:1px solid var(--line); font-size:12px;
style="padding:6px 10px; border:1px solid var(--line); font-size:var(--fs-xs);
color:var(--muted); font-style:italic; text-align:center;">
Antet fara randuri de date
</td>
@@ -69,7 +69,7 @@
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
<div style="margin-bottom:8px; display:flex; align-items:center; gap:10px; flex-wrap:wrap;">
<label for="format-data" style="font-size:13px; color:var(--muted);">
<label for="format-data" style="font-size:var(--fs-sm); color:var(--muted);">
Format data
</label>
<input type="text" id="format-data" name="format_data"
@@ -77,66 +77,97 @@
placeholder="ex: DD.MM.YYYY"
style="max-width:160px;"
aria-describedby="format-data-hint">
<span id="format-data-hint" class="muted" style="font-size:12px;">
<span id="format-data-hint" class="muted" style="font-size:var(--fs-xs);">
sau YYYY-MM-DD, MM/DD/YYYY etc.
</span>
</div>
{% for col in columns %}
{%- set sugg = fuzzy_suggestions.get(col, []) -%}
{%- set best = sugg[0].camp_canonic if sugg else '' -%}
<input type="hidden" name="colname" value="{{ col }}">
<div class="maprow">
<div class="mapcol grow">
<div><strong>{{ col }}</strong></div>
{% if sugg %}
<div class="muted" style="font-size:12px; margin-top:2px;">
sugestie: <span class="sugg">{{ sugg[0].camp_canonic }}
({{ sugg[0].score | round | int }}%)</span>
</div>
{% endif %}
{%- set ns = namespace(samples=[]) -%}
{%- for row in sample_rows -%}
{%- if row.get(col) is not none and row.get(col) != '' -%}
{%- set ns.samples = ns.samples + [row[col] | string] -%}
{%- endif -%}
{%- endfor -%}
{% if ns.samples %}
<div class="muted" style="font-size:11px; margin-top:2px;">
ex: {{ ns.samples[:2] | join(", ") }}
</div>
{% endif %}
</div>
<div class="mapcol" style="min-width:200px;">
<label for="canon-{{ loop.index }}"
style="display:block; font-size:12px; color:var(--muted); margin-bottom:2px;">
Camp canonic
</label>
<select id="canon-{{ loop.index }}" name="canon">
<option value="">— ignorat —</option>
{% for field_key, field_label in canonical_fields %}
<option value="{{ field_key }}"
{% if field_key == best %}selected{% endif %}>
{{ field_key }} — {{ field_label }}
</option>
{# Tabel mapare: coloana din fisier | exemplu | camp RAR (mockup 5.16 / US-013) #}
<div class="tablewrap" style="margin-bottom:16px;">
<table style="border-collapse:collapse; width:100%;">
<thead>
<tr>
<th style="font-size:var(--fs-xs); width:34%; padding:6px 10px; text-align:left;
background:var(--card2); border-bottom:1px solid var(--line); color:var(--muted);
font-weight:600; text-transform:uppercase; letter-spacing:.04em;">
Coloana din fisier
</th>
<th style="font-size:var(--fs-xs); width:28%; padding:6px 10px; text-align:left;
background:var(--card2); border-bottom:1px solid var(--line); color:var(--muted);
font-weight:600; text-transform:uppercase; letter-spacing:.04em;">
Exemplu
</th>
<th style="font-size:var(--fs-xs); padding:6px 10px; text-align:left;
background:var(--card2); border-bottom:1px solid var(--line); color:var(--muted);
font-weight:600; text-transform:uppercase; letter-spacing:.04em;">
Camp RAR
</th>
</tr>
</thead>
<tbody>
{% for col in columns %}
{%- set sugg = fuzzy_suggestions.get(col, []) -%}
{%- set best = sugg[0].camp_canonic if sugg else '' -%}
{%- set ns = namespace(samples=[]) -%}
{%- for row in sample_rows -%}
{%- if row.get(col) is not none and row.get(col) != '' -%}
{%- set ns.samples = ns.samples + [row[col] | string] -%}
{%- endif -%}
{%- endfor -%}
<tr style="border-bottom:1px solid var(--line);">
<td style="padding:9px 10px; vertical-align:top;">
<input type="hidden" name="colname" value="{{ col }}">
<strong style="font-family:var(--font-mono); font-size:var(--fs-sm);">{{ col }}</strong>
{% if sugg %}
<div class="muted" style="font-size:var(--fs-xs); margin-top:3px;">
sugestie: <span class="sugg">{{ sugg[0].camp_canonic }}
({{ sugg[0].score | round | int }}%)</span>
</div>
{% endif %}
</td>
<td style="padding:9px 10px; vertical-align:top;">
{% if ns.samples %}
<span style="font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--muted);">
{{ ns.samples[:2] | join(", ") }}
</span>
{% else %}
<span class="muted" style="font-size:var(--fs-xs);"></span>
{% endif %}
</td>
<td style="padding:9px 10px; vertical-align:top;">
<label for="canon-{{ loop.index }}"
style="display:block; font-size:var(--fs-xs); color:var(--muted); margin-bottom:3px;">
Camp canonic
</label>
<select id="canon-{{ loop.index }}" name="canon"
style="width:100%; font-size:var(--fs-base); min-height:38px;">
<option value="">— ignorat —</option>
{% for field_key, field_label in canonical_fields %}
<option value="{{ field_key }}"
{% if field_key == best %}selected{% endif %}>
{{ field_key }} — {{ field_label }}
</option>
{% endfor %}
</select>
</td>
</tr>
{% endfor %}
</select>
</div>
</tbody>
</table>
</div>
{% endfor %}
<div style="margin-top:16px; display:flex; align-items:center; gap:12px; flex-wrap:wrap;">
<button type="submit"
{% if not prima_inreg %}disabled aria-disabled="true"{% endif %}
style="min-height:44px; padding:10px 24px; font-size:14px;{% if not prima_inreg %} opacity:0.5; cursor:not-allowed;{% endif %}">
style="min-height:44px; padding:10px 24px; font-size:var(--fs-md);{% if not prima_inreg %} opacity:0.5; cursor:not-allowed;{% endif %}">
Salveaza si continua la preview
</button>
{% if not prima_inreg %}
<span style="font-size:12px; color:var(--err);">
<span style="font-size:var(--fs-xs); color:var(--err);">
Fisierul nu contine randuri de date — incarca un fisier cu cel putin o inregistrare.
</span>
{% else %}
<span class="muted" style="font-size:12px;">
<span class="muted" style="font-size:var(--fs-xs);">
maparea se retine pentru fisiere cu acelasi antet
</span>
{% endif %}
@@ -144,7 +175,7 @@
</form>
<div style="margin-top:12px;">
<a href="/" class="muted" style="font-size:13px;">Incarca alt fisier</a>
<a href="/" class="muted" style="font-size:var(--fs-sm);">Incarca alt fisier</a>
</div>
</div>
</div>

View File

@@ -10,11 +10,11 @@
{% set pas = 3 %}{% include '_stepper.html' %}
<div class="card">
<div style="display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:12px;">
<h2 style="font-size:15px; margin:0;">
<h2 style="font-size:var(--fs-md); margin:0;">
Preview —
<span class="muted" style="font-weight:400;">{{ filename or ("import #" ~ import_id) }}</span>
</h2>
<span class="muted" style="margin-left:auto; font-size:13px;">{{ total }} randuri</span>
<span class="muted" style="margin-left:auto; font-size:var(--fs-sm);">{{ total }} randuri</span>
</div>
{% if message %}
@@ -37,7 +37,8 @@
{% for status_key, label in status_labels %}
{%- set cnt = summary.get(status_key, 0) -%}
{% if cnt > 0 %}
<span class="pill s-{{ status_key }}">{{ cnt }} {{ label | lower }}</span>
<span class="pill s-{{ status_key }}" style="display:inline-flex; align-items:center; gap:5px; font-size:var(--fs-xs);">
<span aria-hidden="true" style="display:inline-block; width:7px; height:7px; border-radius:99px; background:currentColor; flex-shrink:0;"></span>{{ cnt }} {{ label | lower }}</span>
{% endif %}
{% endfor %}
</div>
@@ -46,14 +47,14 @@
<div style="display:flex; gap:6px; flex-wrap:wrap; margin-bottom:10px;" role="group"
aria-label="Filtrare dupa stare">
<button type="button" class="filter-btn" data-filter="all"
style="min-height:36px; font-size:13px; padding:4px 12px;">
style="min-height:36px; font-size:var(--fs-sm); padding:4px 12px;">
Toate ({{ total }})
</button>
{% for status_key, label in status_labels %}
{%- set cnt = summary.get(status_key, 0) -%}
{% if cnt > 0 %}
<button type="button" class="filter-btn" data-filter="{{ status_key }}"
style="min-height:36px; font-size:13px; padding:4px 12px;
style="min-height:36px; font-size:var(--fs-sm); padding:4px 12px;
background:transparent; border-color:var(--line); color:var(--ink);">
{{ label }} ({{ cnt }})
</button>
@@ -66,7 +67,7 @@
{% if unmapped_ops %}
<div class="card" style="border-color:var(--err); background:color-mix(in srgb, var(--err) 12%, var(--card)); margin-bottom:14px;">
<h3 style="font-size:14px; margin:0 0 6px;">Operatii de mapat la cod RAR</h3>
<p class="muted" style="margin:0 0 12px; font-size:13px;">
<p class="muted" style="margin:0 0 12px; font-size:var(--fs-sm);">
Aceste operatii din fisier nu au inca un cod RAR. Alege codul (sugestia e
preselectata) si salveaza — randurile blocate trec automat in
<span class="s-ok">ok</span> si maparea se retine pentru fisierele viitoare.
@@ -167,7 +168,7 @@
</table>
<!-- Mesaj "filtrat la zero": afisat de JS cand filtrul ascunde toate randurile -->
<p id="preview-zero-message" class="muted"
style="display:none; text-align:center; padding:24px 16px; font-size:14px;">
style="display:none; text-align:center; padding:24px 16px; font-size:var(--fs-md);">
Niciun rand nu corespunde filtrului selectat.
</p>
</div>
@@ -190,7 +191,7 @@
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
<label for="n-confirmat"
style="font-size:13px; color:var(--muted);">
style="font-size:var(--fs-sm); color:var(--muted);">
Confirma numarul
</label>
<input type="number" id="n-confirmat" name="n_confirmat"
@@ -198,7 +199,7 @@
min="0" required
style="max-width:80px;"
aria-describedby="n-hint">
<span id="n-hint" class="muted" style="font-size:12px;">
<span id="n-hint" class="muted" style="font-size:var(--fs-xs);">
din <span id="n-hint-ok">{{ summary.get('ok', 0) }}</span> gata de trimis
</span>
</div>
@@ -207,13 +208,13 @@ din <span id="n-hint-ok">{{ summary.get('ok', 0) }}</span> gata de trimis
<div style="display:flex; flex-direction:column; gap:6px; align-self:flex-end;">
<button type="submit"
id="confirm-btn"
style="min-height:44px; padding:10px 28px; font-size:14px;"
style="min-height:44px; padding:10px 28px; font-size:var(--fs-md);"
{% if not summary.get('ok', 0) %}disabled title="Niciun rand ok de trimis"{% endif %}>
Trimite la RAR
</button>
{% if summary.get('needs_data', 0) or summary.get('needs_mapping', 0) or summary.get('needs_review', 0) %}
<a href="/v1/import/{{ import_id }}/export-failed" download
style="font-size:12px; text-align:center;">
style="font-size:var(--fs-xs); text-align:center;">
descarca randuri cu probleme (CSV)
</a>
{% endif %}
@@ -226,7 +227,7 @@ din <span id="n-hint-ok">{{ summary.get('ok', 0) }}</span> gata de trimis
<span id="preview-ok-count" data-ok="{{ summary.get('ok', 0) }}" hidden></span>
<div style="padding:8px 0 4px;">
<a href="#" class="muted" style="font-size:13px;"
<a href="#" class="muted" style="font-size:var(--fs-sm);"
hx-get="/_import/reset" hx-target="#import-section" hx-swap="outerHTML">Incarca alt fisier</a>
</div>

View File

@@ -25,7 +25,8 @@
style="{% if status == 'needs_review' %}background:rgba(230,179,74,.04);{% elif _sent_dup %}opacity:.6;{% endif %}">
<td class="col-id muted" data-eticheta="#">{{ row.row_index + 1 }}</td>
<td class="col-stare" data-eticheta="Stare">
<span class="pill {{ row.stare_css }}">{{ row.stare_eticheta }}</span>
<span class="pill {{ row.stare_css }}" style="display:inline-flex; align-items:center; gap:5px;">
<span aria-hidden="true" style="display:inline-block; width:7px; height:7px; border-radius:99px; background:currentColor; flex-shrink:0;"></span>{{ row.stare_eticheta }}</span>
</td>
<td class="col-vehicul" data-eticheta="Vehicul">
{{ row.prez.vehicul_nr }}
@@ -44,7 +45,7 @@
<td class="col-data" data-eticheta="Data prestatie">{{ row.prez.data_prestatie }}</td>
<td class="col-km" data-eticheta="KM final">{{ row.prez.odometru }}</td>
<td class="col-note" data-eticheta="Note"
style="font-size:12px; white-space:normal;">
style="font-size:var(--fs-xs); white-space:normal;">
{% if status == 'already_sent' and row.get('already_sent_info') %}
{% set ai = row.already_sent_info %}
deja trimis {{ (ai.get('created_at') or '')[:10] }}
@@ -78,7 +79,7 @@
style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:12px;">
{% for status_key, label in status_labels %}
{%- set cnt = summary.get(status_key, 0) -%}
{% if cnt > 0 %}<span class="pill s-{{ status_key }}">{{ cnt }} {{ label | lower }}</span>{% endif %}
{% if cnt > 0 %}<span class="pill s-{{ status_key }}" style="display:inline-flex; align-items:center; gap:5px; font-size:var(--fs-xs);"><span aria-hidden="true" style="display:inline-block; width:7px; height:7px; border-radius:99px; background:currentColor; flex-shrink:0;"></span>{{ cnt }} {{ label | lower }}</span>{% endif %}
{% endfor %}
</div>
<span id="preview-ok-count" hx-swap-oob="true" data-ok="{{ summary.get('ok', 0) }}" hidden></span>

View File

@@ -14,55 +14,84 @@
</div>
{% endif %}
{# === D6: Strip sanatate mereu-vizibil DEASUPRA contoarelor ===
Verde: worker viu + RAR ok → "Declaratiile curg normal"
Rosu: worker oprit SAU RAR inaccesibil → "Blocat: ... — declaratiile NU pleaca"
Glife accesibile ✓/✗ (nu doar culoare). Layout: glifa+text stanga, ultima auth dreapta.
{# US-006 (5.17) — Banner one-time trial->Gratuit (T-DES-1): afisat la prima incarcare
dupa expirarea trial-ului. Discret, non-blocant; dismissibil via sessionStorage.
Nu acopera stripul de sanatate (apare inainte de health strip, la acelasi nivel). #}
{% if trial_expirat_recent|default(false) %}
<div id="banner-trial-expirat"
role="status"
style="margin-bottom:10px; padding:7px 12px;
border-left:3px solid var(--warn);
background:color-mix(in srgb, var(--warn) 10%, var(--card));
border-radius:6px; font-size:var(--fs-sm);
display:flex; align-items:center; justify-content:space-between; gap:8px;">
<span>Trial Pro expirat — esti pe Gratuit, 60/luna</span>
<button onclick="sessionStorage.setItem('tfx','1'); document.getElementById('banner-trial-expirat').style.display='none';"
style="background:transparent; border:none; color:var(--muted); cursor:pointer;
font-size:18px; padding:0 4px; line-height:1; flex-shrink:0;"
aria-label="Inchide bannerul">×</button>
</div>
<script>(function(){ if(sessionStorage.getItem('tfx')){ var el=document.getElementById('banner-trial-expirat'); if(el) el.style.display='none'; } })();</script>
{% endif %}
{# === US-003 (PRD 5.16): Banda de stare RAR — NUMAI cand BLOCAT (rosu, lat de 100%).
OK = dot verde in antet (base.html); banda nu mai apare cand totul e ok.
Elementul id="strip-sanatate" ramane in DOM mereu, dar goleste continutul cand OK,
astfel "hidden" + fara continut eroare in sursa = nu pica testele de prezenta id-ului.
#}
{% if sanatate_ok %}
<div id="strip-sanatate" role="status" aria-live="polite" hidden></div>
{% else %}
<div id="strip-sanatate"
role="status"
aria-live="polite"
style="display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
padding:10px 14px; border-radius:8px; margin-bottom:14px;
{% if sanatate_ok %}background:color-mix(in srgb, var(--ok) 13%, transparent); border:1px solid color-mix(in srgb, var(--ok) 30%, transparent);
{% else %}background:color-mix(in srgb, var(--err) 16%, var(--card)); border:1px solid color-mix(in srgb, var(--err) 40%, transparent);
{% endif %}">
background:color-mix(in srgb, var(--err) 16%, var(--card)); border:1px solid color-mix(in srgb, var(--err) 40%, transparent);">
<div style="display:flex; align-items:center; gap:9px;">
{% if sanatate_ok %}
<span aria-hidden="true" style="font-weight:700; font-size:15px; color:var(--ok);">&#10003;</span>
{% else %}
<span aria-hidden="true" style="font-weight:700; font-size:15px; color:var(--err);">&#10007;</span>
{% endif %}
<span style="font-weight:700; font-size:13px;">{{ sanatate_text }}</span>
</div>
<span style="font:400 11px/1.4 'IBM Plex Mono',ui-monospace,monospace; color:var(--muted); white-space:nowrap;">
<span style="font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--muted); white-space:nowrap;">
{{ eticheta_ultima_auth }}: {{ last_login }}
</span>
</div>
{% endif %}
{# === D4: 3 carduri-contor (mockup exact: Trimise / In coada / De corectat) ===
Responsive: flex-wrap => 3 pe rand desktop, 2/stivuite pe mobil (min-width:120px).
Trimise: all-time (cifra mare) + sub-linie "luna N · azi N" (D4 + E7).
De corectat: rosu cand >0 (s-error), muted cand 0.
{# === US-002 (PRD 5.16): 5 carduri-contor separate (desktop) + bara compacta (mobil <=560px).
Total / Luna asta / Azi / In coada / De corectat.
#}
<div style="display:flex; gap:12px; flex-wrap:wrap; margin-bottom:14px;">
{# Desktop: 5 carduri side-by-side. display:flex + layout stau in CSS (.contoare-desktop in
base.html), NU inline, ca media query-ul <=560px sa le poata ascunde pe mobil (bara compacta). #}
<div class="contoare-desktop">
{# Trimise (all-time principal, luna/azi secundar) #}
<div class="contor-card" style="flex:1; min-width:120px;">
{# Total trimise (all-time) #}
<div class="contor-card" style="flex:1; min-width:100px;">
<div class="contor-cifra">{{ counts_sent }}</div>
<div class="contor-label">Trimise (total)</div>
<div class="contor-sub">luna {{ sent_month }} &middot; azi {{ sent_today }}</div>
<div class="contor-label">Total</div>
</div>
{# In coada (accent/albastru) #}
<div class="contor-card" style="flex:1; min-width:120px;">
{# Luna asta #}
<div class="contor-card" style="flex:1; min-width:100px;">
<div class="contor-cifra s-accent">{{ sent_month }}</div>
<div class="contor-label">Luna asta</div>
</div>
{# Azi #}
<div class="contor-card" style="flex:1; min-width:80px;">
<div class="contor-cifra s-accent">{{ sent_today }}</div>
<div class="contor-label">Azi</div>
</div>
{# In coada #}
<div class="contor-card" style="flex:1; min-width:80px;">
<div class="contor-cifra s-queued">{{ counts_queued }}</div>
<div class="contor-label">In coada</div>
</div>
{# De corectat (rosu daca >0, muted la 0; link catre lista) #}
<a href="/" class="contor-card"
style="flex:1; min-width:120px; text-decoration:none; display:block; cursor:pointer;"
style="flex:1; min-width:80px; text-decoration:none; display:block; cursor:pointer;"
aria-label="De corectat: {{ blocate_total }} — click pentru lista de trimiteri">
<div class="contor-cifra {{ 's-error' if blocate_total else 'muted' }}">{{ blocate_total }}</div>
<div class="contor-label">De corectat</div>
@@ -70,6 +99,30 @@
</div>
{# Mobil (<=560px): bara compacta — numerele + etichete scurte in-line #}
<div class="contoare-compact">
<div class="compact-item">
<div class="compact-nr">{{ counts_sent }}</div>
<div class="compact-lbl">Total</div>
</div>
<div class="compact-item">
<div class="compact-nr s-accent">{{ sent_month }}</div>
<div class="compact-lbl">Luna</div>
</div>
<div class="compact-item">
<div class="compact-nr s-accent">{{ sent_today }}</div>
<div class="compact-lbl">Azi</div>
</div>
<div class="compact-item">
<div class="compact-nr s-queued">{{ counts_queued }}</div>
<div class="compact-lbl">Coada</div>
</div>
<a class="compact-item" href="/" style="text-decoration:none; color:inherit;">
<div class="compact-nr {{ 's-error' if blocate_total else 'muted' }}">{{ blocate_total }}</div>
<div class="compact-lbl">Erori</div>
</a>
</div>
{# === Navigatie rapida: Trimiteri + Mapari cu badge needs_mapping ===
Pastrata exact ca inainte (US-005): tab_activ determina marcajul activ.
#}
@@ -84,4 +137,20 @@
class="status-nav-link{% if _tab == 'mapari' %} status-nav-activ{% endif %}">Mapari{% if mapari_badge %}<span class="tab-badge" aria-hidden="true" style="display:inline-flex; align-items:center; justify-content:center; min-width:16px; height:16px; margin-left:4px; padding:0 4px; border-radius:99px; background:var(--err); color:#fff; font-size:11px; font-weight:700;">{{ mapari_badge }}</span>{% endif %}</a>
</nav>
{# US-006 (5.17): linia de plan — consum/trial (secundar, sub navigatie, non-blocant).
Warn=culoare+text (accesibilitate): >=80% -> --warn; limita atinsa -> --err.
Ierarhie: nu concureaza cu stripul de sanatate (E zero-silent-failures pastrat). #}
{% if plan_linie is defined and plan_linie %}
<div class="plan-status-line"
style="font-size:var(--fs-sm); margin-top:6px; padding-top:6px;
border-top:1px solid var(--line2);
color:{% if plan_limita_atinsa|default(false) %}var(--err){% elif plan_warn|default(false) %}var(--warn){% else %}var(--muted){% endif %};
{% if plan_warn|default(false) %}font-weight:600;{% endif %}">
{{ plan_linie }}
{% if plan_limita_atinsa|default(false) or plan_warn|default(false) %}
&nbsp;<a href="/?tab=cont" style="font-size:var(--fs-xs); font-weight:400; color:var(--accent);">Detalii plan</a>
{% endif %}
</div>
{% endif %}
</div>

View File

@@ -52,15 +52,15 @@
role="region" aria-label="Zona de incarcare fisier"
style="display:flex; align-items:center; gap:14px; flex-wrap:wrap;
padding:12px 16px; text-align:left;">
<strong style="font-size:14px;">Importa:</strong>
<strong style="font-size:var(--fs-md);">Importa:</strong>
<input id="file-input" type="file" name="file" accept=".xlsx,.xls,.csv"
style="display:none;" aria-label="Selecteaza fisier xlsx sau csv">
<button type="button" id="upload-btn"
style="min-height:44px; padding:10px 20px; font-size:14px;">
style="min-height:44px; padding:10px 20px; font-size:var(--fs-md);">
Alege fisier (xlsx/csv)
</button>
<span class="muted" style="font-size:13px;">sau trage aici</span>
<span class="muted" style="font-size:12px; margin-left:auto;">
<span class="muted" style="font-size:var(--fs-sm);">sau trage aici</span>
<span class="muted" style="font-size:var(--fs-xs); margin-left:auto;">
NU se trimite nimic la RAR pana confirmi.
</span>
</div>
@@ -69,10 +69,10 @@
<div class="drop-zone" id="drop-zone"
role="region" aria-label="Zona de incarcare fisier">
{% if not sheets %}
<p style="font-size:17px; margin:0 0 4px; font-weight:600;">Primul fisier? Trage-l aici.</p>
<p class="muted" style="margin:0 0 16px; font-size:13px;">xlsx sau csv, max 5000 randuri</p>
<p style="font-size:var(--fs-lg); margin:0 0 4px; font-weight:600;">Primul fisier? Trage-l aici.</p>
<p class="muted" style="margin:0 0 16px; font-size:var(--fs-sm);">xlsx sau csv, max 5000 randuri</p>
{% else %}
<p class="muted" style="margin:0 0 16px; font-size:14px;">
<p class="muted" style="margin:0 0 16px; font-size:var(--fs-md);">
Incarca fisierul din nou dupa ce ai ales foaia.
</p>
{% endif %}
@@ -80,18 +80,18 @@
<input id="file-input" type="file" name="file" accept=".xlsx,.xls,.csv"
style="display:none;" aria-label="Selecteaza fisier xlsx sau csv">
<button type="button" id="upload-btn"
style="min-height:44px; padding:10px 24px; font-size:14px;">
style="min-height:44px; padding:10px 24px; font-size:var(--fs-md);">
Alege fisier (xlsx/csv)
</button>
</div>
<p class="muted" style="margin:8px 0 0; font-size:12px;">
<p class="muted" style="margin:8px 0 0; font-size:var(--fs-xs);">
NU se trimite nimic la RAR pana confirmi explicit.
</p>
{% endif %}
<span id="upload-spinner" class="htmx-indicator muted"
style="font-size:13px; margin-top:6px; display:inline;">
style="font-size:var(--fs-sm); margin-top:6px; display:inline;">
se parseaza fisierul...
</span>
</form>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Gateway RAR AUTOPASS{% endblock %}</title>
<title>{% block title %}ROMFAST AUTOPASS{% endblock %}</title>
<script src="/static/htmx.min.js"></script>
<script>
// Raspunsurile de editare-rand contin un <tr> (swap pe rand) PLUS
@@ -36,78 +36,22 @@
})();
</script>
<style>
/* IBM Plex Sans + Mono self-hosted (latin-ext pentru diacritice romanesti).
font-display:swap permite text vizibil inainte de incarcare (FOUT system-ui->IBM Plex);
reflow-ul vizibil pe VIN/coduri e acceptat explicit. */
@font-face {
font-family: "IBM Plex Sans";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/static/fonts/IBMPlexSans-Regular-latin-ext.woff2") format("woff2");
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF;
}
@font-face {
font-family: "IBM Plex Sans";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/static/fonts/IBMPlexSans-Regular-latin.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+20AC, U+2122, U+FEFF, U+FFFD;
}
@font-face {
font-family: "IBM Plex Sans";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("/static/fonts/IBMPlexSans-Medium-latin-ext.woff2") format("woff2");
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF;
}
@font-face {
font-family: "IBM Plex Sans";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("/static/fonts/IBMPlexSans-Medium-latin.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+20AC, U+2122, U+FEFF, U+FFFD;
}
@font-face {
font-family: "IBM Plex Sans";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("/static/fonts/IBMPlexSans-Bold-latin-ext.woff2") format("woff2");
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF;
}
@font-face {
font-family: "IBM Plex Sans";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("/static/fonts/IBMPlexSans-Bold-latin.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+20AC, U+2122, U+FEFF, U+FFFD;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/static/fonts/IBMPlexMono-Regular-latin-ext.woff2") format("woff2");
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF;
}
@font-face {
font-family: "IBM Plex Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/static/fonts/IBMPlexMono-Regular-latin.woff2") format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+20AC, U+2122, U+FEFF, U+FFFD;
}
/* US-001 PRD 5.16: stive de font standard web. Toate regulile font-face IBM Plex sterse.
Motiv: decizie user (risc AI-Slop #11 acceptat constient), uniformitate cross-page.
Fisierele woff2 raman pe disc (curatare = follow-up optional, non-blocant).
Referinte catre directorul de fonturi statice eliminate — font-ui si font-mono sunt stive sistem. */
/* Paleta dark (default) — accent azur ROMFAST.
--card2: fundal input/contor (= --bg, nivelul cel mai adanc).
--line2: separator subtire (intre --bg si --line). */
:root { --bg:#0f1218; --card:#181c24; --card2:#0f1218; --ink:#e6e9ef; --muted:#8b93a7; --line:#262b36; --line2:#1f2530;
--ok:#2FBF8F; --warn:#E0A93B; --err:#E05D5D; --accent:#2E74D6; }
--ok:#2FBF8F; --warn:#E0A93B; --err:#E05D5D; --accent:#2E74D6;
/* US-001 (PRD 5.16): stive font standard web — sursa unica de adevar */
--font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Segoe UI Mono", "Roboto Mono", Menlo, Consolas, monospace;
/* US-002 (PRD 5.16): scala tipografica uniforma — sursa unica de adevar */
--fs-xs:12px; --fs-sm:13.5px; --fs-base:15px; --fs-md:16px;
--fs-lg:18px; --fs-xl:20px; --fs-2xl:28px; --fs-3xl:34px;
--lh-tight:1.25; --lh-body:1.55; }
/* Paleta light — accent azur inchis pentru contrast AA pe alb (#1F66C9: 5.51:1 pe alb) */
[data-theme="light"] { --bg:#f5f7fa; --card:#ffffff; --card2:#f5f7fa; --ink:#1a1d24; --muted:#5c6473; --line:#e2e5ea; --line2:#eaedf2;
--ok:#15803d; --warn:#b45309; --err:#dc2626; --accent:#1F66C9; }
@@ -135,7 +79,7 @@
variabila; folosim consecvent `@media (max-width:767px)` peste tot (mobil) si
`@media (max-width:1024px)` doar pentru densitatea tabelului. >=1024px = layout
desktop neschimbat (fara regresie). Orice regula mobila noua reutilizeaza 767px. */
body { margin:0; font:15px/1.5 "IBM Plex Sans",system-ui,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
body { margin:0; font-family:var(--font-ui); font-size:var(--fs-base); line-height:var(--lh-body);
background:var(--bg); color:var(--ink); -webkit-font-smoothing:antialiased; }
/* Grila 3 coloane — stanga (logo ROMFAST) | centru (titlu+env) | dreapta (controale). */
header { padding:16px 24px; border-bottom:1px solid var(--line);
@@ -200,6 +144,44 @@
flex-wrap:wrap; z-index:10; }
/* Indicator HTMX — ascuns pana la request */
.htmx-indicator { display:none; }
/* US-011 (PRD 5.16): selector tema stil pill — icon + eticheta temei curente.
Eticheta se ascunde pe <=560px (spatiu ingust), ramane iconita. */
.tema-btn { display:inline-flex; align-items:center; gap:6px; height:36px; padding:0 12px;
border-radius:8px; background:transparent; border:1px solid var(--line);
color:var(--muted); font-family:var(--font-ui); font-size:var(--fs-sm);
cursor:pointer; transition:border-color .15s, color .15s; line-height:1; }
.tema-btn:hover { border-color:var(--accent); color:var(--ink); }
.tema-btn:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
@media (max-width:560px) { #tema-label { display:none; } }
/* US-003 (PRD 5.16): dot RAR compact in antet.
Stare OK: dot verde pulsant + "RAR online". Stare BLOCAT: dot rosu.
Stilat ca pill; sensul NU depinde de culoare (aria-label + title). */
.rar-chip { display:inline-flex; align-items:center; gap:7px; height:36px; padding:0 12px;
border-radius:99px; font-size:var(--fs-sm); font-weight:600; cursor:default; white-space:nowrap; }
.rar-chip.rar-ok { border:1px solid color-mix(in srgb,var(--ok) 35%,var(--line));
background:color-mix(in srgb,var(--ok) 10%,transparent); color:var(--ok); }
.rar-chip.rar-err { border:1px solid color-mix(in srgb,var(--err) 35%,var(--line));
background:color-mix(in srgb,var(--err) 10%,transparent); color:var(--err); }
.rar-dot { width:9px; height:9px; border-radius:99px; background:currentColor; flex-shrink:0;
box-shadow:0 0 0 3px color-mix(in srgb,currentColor 20%,transparent); }
.rar-dot.live { animation:rar-pulse 2s ease-in-out infinite; }
@keyframes rar-pulse { 0%,100%{opacity:1;} 50%{opacity:.5;} }
@media (max-width:560px) { .rar-chip .rar-tx { display:none; } }
/* US-010 (PRD 5.16): sub-titlu cu numele service-ului (cand autentificat). */
.h-sub { font-size:var(--fs-xs); color:var(--muted); margin-top:2px; line-height:1.2; }
.h-sub .svc { color:var(--ink); font-weight:600; }
/* Badge env (test/prod) si badge tier (plan cont) langa titlu. */
.badge-env { display:inline-block; margin-left:8px; padding:1px 7px; border-radius:99px;
font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
color:var(--warn); background:color-mix(in srgb,var(--warn) 16%,transparent); vertical-align:middle; }
.badge-tier { display:inline-block; margin-left:6px; padding:1px 8px; border-radius:99px;
font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
color:var(--accent); background:color-mix(in srgb,var(--accent) 16%,transparent); vertical-align:middle; }
/* Menu RAR status line in burger (prima intrare) */
.menu-rar-line { display:flex; align-items:center; gap:7px; padding:8px 10px;
font-size:var(--fs-sm); border-radius:6px; cursor:default; }
.menu-rar-line.ok { color:var(--ok); }
.menu-rar-line.err { color:var(--err); background:color-mix(in srgb,var(--err) 6%,transparent); }
.htmx-indicator.htmx-request { display:inline; }
/* Link-uri de actiune in antetul cardurilor: zona de atins mai mare (>=36px) si
feedback la hover; pe ecrane inguste antetul se rupe curat sub titlu. */
@@ -317,7 +299,7 @@
border-radius:0 6px 6px 0; }
.eroare-3n-sep { margin-top:6px; }
.eroare-3n-problema { font-weight:600; color:var(--err); font-size:13px; }
.eroare-3n-camp { font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:12px; opacity:.85; }
.eroare-3n-camp { font-family:var(--font-mono); font-size:var(--fs-xs); opacity:.85; }
.eroare-3n-cauza { color:var(--muted); font-size:12px; margin-top:3px; }
.eroare-3n-fix { color:var(--accent); font-size:12px; margin-top:3px; }
.eroare-3n-label { font-weight:500; }
@@ -424,8 +406,8 @@
/* secundarul muted („cod RAR" / „nemapat") — >=12px, contrast pe var(--muted) >=4.5:1 */
.tabel-trimiteri .cod-rar-sub { font-size:12px; margin-top:2px; }
/* Codul RAR pe linia 2 — chip discret, fara prefixul „cod RAR:". */
.tabel-trimiteri .cod-rar-cod { display:inline-block; font-family:"IBM Plex Mono",ui-monospace,monospace;
font-size:12px; padding:1px 7px; border:1px solid var(--line);
.tabel-trimiteri .cod-rar-cod { display:inline-block; font-family:var(--font-mono);
font-size:var(--fs-xs); padding:1px 7px; border:1px solid var(--line);
border-radius:99px; color:var(--muted); }
/* Eticheta umana scurta sub pill — text mic; clasa `s-error` o coloreaza
(apare doar pe error/needs_*). Stare prin text, nu doar culoare. */
@@ -696,37 +678,56 @@
}
/* === SENTINEL-COMPONENTE-SLIM: inceput componente slim US-002 (PRD 5.15).
Testele ancoreaza pe acest marker. Nu muta/sterge. === */
/* .contor-card — card cifra contor: fundal --card2, bordura --line, radius 8px, padding 10-12px.
/* .contor-card — card cifra contor: fundal --card2, bordura --line, radius 8px.
US-002 PRD 5.16: padding marit (18px), cifra pe --fs-2xl, label pe --fs-sm, sub pe --fs-xs.
Variante de culoare a cifrei prin clasele .s-* existente (verde/accent/rosu). */
.contor-card { background:var(--card2); border:1px solid var(--line); border-radius:8px; padding:10px 12px; }
.contor-cifra { font-size:22px; font-weight:700; line-height:1; }
.contor-label { font-size:11px; color:var(--muted); margin-top:5px; }
.contor-sub { font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:10px; color:var(--muted); margin-top:3px; }
.contor-card { background:var(--card2); border:1px solid var(--line); border-radius:8px; padding:18px 18px; }
.contor-cifra { font-size:var(--fs-2xl); font-weight:700; line-height:1; }
.contor-label { font-size:var(--fs-sm); color:var(--muted); margin-top:8px; }
.contor-sub { font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--muted); margin-top:4px; }
/* Contoarele desktop = 5 carduri side-by-side. display:flex sta in CSS (NU inline pe
element) ca media query-ul de mai jos sa-l poata ascunde pe mobil — un inline
style="display:flex" ar bate regula @media si ar duce la contoare duplicate pe 390px. */
.contoare-desktop { display:flex; gap:10px; flex-wrap:wrap; margin-bottom:14px; }
/* Bara compacta contoare pe mobil (<=560px): un singur rand, numere + etichete scurte, fara carduri mari. */
.contoare-compact { display:none; }
@media (max-width:560px) {
.contoare-desktop { display:none; }
.contoare-compact { display:flex; align-items:center; gap:0; margin-bottom:14px;
background:var(--card2); border:1px solid var(--line); border-radius:8px;
overflow:hidden; }
.compact-item { flex:1; display:flex; flex-direction:column; align-items:center; padding:10px 6px;
border-right:1px solid var(--line); min-width:0; text-align:center; }
.compact-item:last-child { border-right:none; }
.compact-nr { font-size:var(--fs-xl); font-weight:700; line-height:1; }
.compact-lbl { font-size:10px; color:var(--muted); margin-top:3px; white-space:nowrap; }
}
/* .lista-trimiteri-slim + .trimitere-slim — lista compacta cu separator --line2.
Randul e clickabil (rol button), tinta min-height:44px pe mobil. */
.lista-trimiteri-slim { list-style:none; margin:0; padding:0; }
.trimitere-slim { display:flex; align-items:center; justify-content:space-between; gap:12px;
padding:11px 14px; border-bottom:1px solid var(--line2); min-height:44px; cursor:pointer; }
padding:14px 16px; border-bottom:1px solid var(--line2); min-height:44px; cursor:pointer; }
.trimitere-slim:last-child { border-bottom:none; }
.trimitere-slim:hover { background:color-mix(in srgb, var(--accent) 6%, transparent); }
.trimitere-slim:focus, .trimitere-slim:focus-visible { outline:2px solid var(--accent); outline-offset:-2px; }
.slim-vin { font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:13px; font-weight:500; color:var(--ink); }
.slim-meta { font-size:11px; color:var(--muted); margin-top:3px; }
/* .camp-slim — varianta compacta camp formular: label 11px muted deasupra, input ~30px, fundal --card2.
.slim-vin { font-family:var(--font-mono); font-size:var(--fs-md); font-weight:500; color:var(--ink); }
.slim-meta { font-size:var(--fs-sm); color:var(--muted); margin-top:3px; }
/* .camp-slim — varianta compacta camp formular: label --fs-sm muted deasupra, input --fs-md, fundal --card2.
Mono pentru campuri VIN/odometru/nr: adauga clasa .camp-mono pe input. */
.camp-slim { margin-bottom:8px; }
.camp-slim label { font-size:11px; color:var(--muted); display:block; margin-bottom:4px; }
.camp-slim input, .camp-slim textarea, .camp-slim select { background:var(--card2); height:30px; width:100%;
padding:0 10px; border:1px solid var(--line); border-radius:6px; font:inherit; color:var(--ink); }
.camp-slim textarea { height:auto; min-height:48px; padding:8px 10px; resize:vertical; }
.camp-slim .camp-mono { font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:12px; }
.camp-slim label { font-size:var(--fs-sm); color:var(--muted); display:block; margin-bottom:4px; }
.camp-slim input, .camp-slim textarea, .camp-slim select { background:var(--card2); min-height:36px; width:100%;
padding:0 10px; border:1px solid var(--line); border-radius:6px; font-family:var(--font-ui);
font-size:var(--fs-md); color:var(--ink); }
.camp-slim textarea { min-height:52px; height:auto; padding:8px 10px; resize:vertical; }
.camp-slim .camp-mono { font-family:var(--font-mono); font-size:var(--fs-sm); }
/* .chips + .chip — prestatii multi-select cu buton de stergere accesibil (.chip-del).
Fundal accent 18%, font IBM Plex Mono 11px. */
Fundal accent 18%, font mono --fs-xs. */
.chips { min-height:30px; display:flex; align-items:center; gap:6px; flex-wrap:wrap;
padding:4px 8px; border:1px solid var(--line); border-radius:6px; background:var(--card2); }
.chip { display:inline-flex; align-items:center; gap:5px; padding:3px 8px; border-radius:5px;
background:color-mix(in srgb, var(--accent) 18%, transparent); color:var(--accent);
font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:11px; font-weight:600; }
font-family:var(--font-mono); font-size:var(--fs-xs); font-weight:600; }
.chip .chip-del { background:transparent; border:none; color:inherit; opacity:.7; cursor:pointer;
padding:0; font-size:13px; line-height:1; display:inline-flex;
align-items:center; justify-content:center; min-width:16px; min-height:16px; }
@@ -737,13 +738,13 @@
/* .add-code — buton dashed pentru adaugare cod in chipbox */
.add-code { display:inline-flex; align-items:center; height:22px; padding:0 7px; background:transparent;
border:1px dashed color-mix(in srgb, var(--accent) 55%, var(--line));
border-radius:5px; color:var(--accent); font:500 10px inherit; cursor:pointer; }
border-radius:5px; color:var(--accent); font:500 10px var(--font-ui); cursor:pointer; }
.add-code:hover, .add-code:focus-visible { border-style:solid; }
/* .op-row — rand operatie cu picker op<->cod (E4): operatie + chip cod + picker */
.op-row { display:flex; align-items:center; justify-content:space-between; gap:10px;
padding:8px 10px; border:1px solid var(--line); border-radius:6px;
background:var(--card2); margin-bottom:8px; }
.op-row-name { font-size:12px; font-weight:500; color:var(--ink); }
.op-row-name { font-size:var(--fs-xs); font-weight:500; color:var(--ink); }
.op-row-warn { border-color:color-mix(in srgb, var(--warn) 45%, var(--line)); }
/* Mobil: tinta touch pentru trimitere-slim (deja garantata prin min-height:44px in regula de baza) */
@media (max-width:767px) {
@@ -753,7 +754,9 @@
</style>
</head>
<body>
{# Grila 3 coloane — stanga (logo ROMFAST) | centru (titlu+env) | dreapta (controale). #}
{# US-010 (PRD 5.16): antet branduit ROMFAST AUTOPASS.
Grila 3 coloane — stanga (logo) | centru (titlu+env+tier+account_name) | dreapta (RAR dot + tema + burger).
Antet MINIMAL pe /login: nu afiseaza RAR dot, meniu burger sau account_name (nelogat). #}
<header>
{# Celula stanga: logo ROMFAST #}
<div class="header-left">
@@ -763,35 +766,86 @@
<img src="/static/romfast_logo.png" alt="ROMFAST" class="brand-logo">
</a>
</div>
{# Celula centru: titlu + badge env mic.
{# Celula centru: titlu ROMFAST AUTOPASS + badge env + badge tier + sub-titlu account_name.
Titlul linkeaza la / (Trimiteri) ca si logo-ul. #}
<div class="header-center">
<a href="/" style="text-decoration:none; color:inherit;"><h1>Gateway RAR AUTOPASS</h1></a>
<span class="env">{{ rar_env }}</span>
<a href="/" style="text-decoration:none; color:inherit;">
<h1>ROMFAST AUTOPASS<span class="badge-env">{{ rar_env }}</span>{% if is_authenticated|default(false) and tier_label|default('') %}<span class="badge-tier">{{ tier_label }}</span>{% endif %}</h1>
</a>
{% if is_authenticated|default(false) and account_name|default('') %}
<div class="h-sub">Service auto: <span class="svc">{{ account_name }}</span></div>
{% endif %}
</div>
{# Celula dreapta: comutator tema + versiune + meniu cont #}
{# Celula dreapta: dot RAR (numai cand logat) + selector tema + versiune + meniu burger #}
<div class="header-right">
<button id="tema-toggle" class="icon-btn"
aria-label="Comuta tema (luminos/intunecat)"
title="Comuta tema">&#9728;</button>
<span class="muted" style="font-size:13px;">v{{ version }}</span>
{# US-003 (PRD 5.16): dot RAR in antet — OK = chip verde pulsant, BLOCAT = chip rosu.
Banda plina apare DOAR in _status.html cand BLOCAT (nu mai e mereu vizibila). #}
{% if is_authenticated|default(false) %}
{% if sanatate_ok|default(true) %}
<div class="rar-chip rar-ok" role="status"
title="RAR online{% if last_login|default('') %} — Ultima autentificare: {{ last_login }}{% endif %}"
aria-label="RAR online">
<span class="rar-dot live" aria-hidden="true"></span>
<span class="rar-tx">RAR online</span>
</div>
{% else %}
<div class="rar-chip rar-err" role="status"
title="RAR indisponibil"
aria-label="RAR indisponibil">
<span class="rar-dot" aria-hidden="true"></span>
<span class="rar-tx">RAR blocat</span>
</div>
{% endif %}
{% endif %}
{# US-011 (PRD 5.16): selector tema = pill cu icon + eticheta temei curente.
Eticheta ascunsa pe <=560px via CSS. JS actualizeaza .tema-icon si #tema-label. #}
<button id="tema-toggle" class="tema-btn"
aria-label="Comuta tema"
title="Comuta tema">
<span class="tema-icon" aria-hidden="true">&#9728;</span>
<span id="tema-label">Light</span>
</button>
<span class="muted" style="font-size:var(--fs-xs);">v{{ version }}</span>
{% if is_authenticated|default(false) %}
{# Meniu cont: Cont/Integrare/Nomenclator + (admin) + logout.
US-010: structura cu <hr> separatori + RAR status (prima intrare) + Plan tier.
Pe paginile neautentificate (login/signup) nu se randeaza deloc. #}
<div class="cont-menu-wrap">
<button id="cont-menu-toggle" class="icon-btn"
aria-haspopup="true" aria-expanded="false" aria-controls="cont-menu"
aria-label="Meniu cont" title="Meniu cont">&#9776;</button>
<div id="cont-menu" class="cont-menu" role="menu" aria-labelledby="cont-menu-toggle" hidden>
{# Prima intrare: Trimiteri (Acasa) — pagina principala cu import + lista trimiterilor. #}
{# Prima intrare: starea RAR (US-003) #}
{% if sanatate_ok|default(true) %}
<div class="menu-rar-line ok" role="menuitem" aria-disabled="true">
<span style="width:8px;height:8px;border-radius:99px;background:currentColor;display:inline-block;"></span>
RAR online
</div>
{% else %}
<div class="menu-rar-line err" role="menuitem" aria-disabled="true">
<span style="width:8px;height:8px;border-radius:99px;background:currentColor;display:inline-block;"></span>
RAR indisponibil
</div>
{% endif %}
{# Plan cont curent (US-006 PRD 5.17): linie detaliata cu trial/consum/warn.
Warn = culoare + text (accesibilitate, decizie #14). #}
<div class="menu-rar-line" role="menuitem" aria-disabled="true"
style="color:{% if plan_limita_atinsa|default(false) %}var(--err){% elif plan_warn|default(false) %}var(--warn){% else %}var(--muted){% endif %};
{% if plan_warn|default(false) %}font-weight:600;{% endif %}">
{{ plan_linie|default('Plan: ' + (tier_label|default('Gratuit'))) }}
</div>
<hr>
{# Navigare principala: Trimiteri + Mapari #}
<a role="menuitem" href="/">Trimiteri</a>
{# Mapari, cu badge needs_mapping. #}
{% set _mapari_badge = (badges.mapari if (badges is defined and badges and badges.mapari) else 0) %}
<a role="menuitem" href="/?tab=mapari">Mapari{% if _mapari_badge %}<span class="tab-badge" aria-hidden="true" style="display:inline-flex; align-items:center; justify-content:center; min-width:18px; height:18px; margin-left:6px; padding:0 5px; border-radius:99px; background:var(--err); color:#fff; font-size:11px; font-weight:700;">{{ _mapari_badge }}</span>{% endif %}</a>
<hr>
{# Nomenclator: coduri RAR — public, dar in meniu arata mai logic la logat #}
<a role="menuitem" href="/?tab=nomenclator">Nomenclator</a>
<hr>
{# Setari cont #}
<a role="menuitem" href="/?tab=cont">Cont</a>
<a role="menuitem" href="/?tab=integrare">Integrare</a>
<a role="menuitem" href="/?tab=nomenclator">Nomenclator</a>
<a role="menuitem" href="/?tab=jurnal">Jurnal</a>
{% if is_admin|default(false) %}<a role="menuitem" href="/admin">Conturi clienti</a>{% endif %}
<hr>
@@ -863,7 +917,11 @@
}
function _syncButton(stored) {
var s = VALID[stored] ? stored : 'auto';
btn.innerHTML = ICONS[s];
// US-011: actualizeaza iconita si eticheta separat (btn e pill, nu se inlocuieste innerHTML intreg)
var icon = btn.querySelector('.tema-icon');
if (icon) icon.innerHTML = ICONS[s];
var label = document.getElementById('tema-label');
if (label) label.textContent = LABELS[s];
btn.setAttribute('aria-label', 'Tema: ' + LABELS[s] + ', apasa pentru ' + NEXT[s]);
btn.title = LABELS[s]; // doar numele temei (ex. "Petrol"), nu ciclul intreg
}
@@ -1129,7 +1187,7 @@
// Inchidere: x si backdrop (elemente cu data-modal-close), Esc.
overlay.addEventListener('click', function(e) {
if (e.target && e.target.hasAttribute && e.target.hasAttribute('data-modal-close')) close();
if (e.target && e.target.closest && e.target.closest('[data-modal-close]')) close();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && isOpen()) { e.preventDefault(); close(); }

View File

@@ -4,21 +4,15 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Gateway RAR AUTOPASS — declară automat la RAR | ROMFAST</title>
<meta name="description" content="Gateway web care declară prestațiile de service-auto la RAR AUTOPASS, automat. Conform Legii 142/2023. Gratuit până la 100 de prestații/lună, fără card bancar.">
<meta name="description" content="Gateway web care declară prestațiile de service-auto la RAR AUTOPASS, automat. Conform Legii 142/2023. Gratuit până la 60 de prestații/lună, fără card bancar.">
<style>
@font-face{font-family:"IBM Plex Sans";font-weight:400;font-display:swap;src:url("/static/fonts/IBMPlexSans-Regular-latin-ext.woff2") format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF;}
@font-face{font-family:"IBM Plex Sans";font-weight:400;font-display:swap;src:url("/static/fonts/IBMPlexSans-Regular-latin.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+20AC,U+2122;}
@font-face{font-family:"IBM Plex Sans";font-weight:500;font-display:swap;src:url("/static/fonts/IBMPlexSans-Medium-latin-ext.woff2") format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF;}
@font-face{font-family:"IBM Plex Sans";font-weight:500;font-display:swap;src:url("/static/fonts/IBMPlexSans-Medium-latin.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+20AC,U+2122;}
@font-face{font-family:"IBM Plex Sans";font-weight:700;font-display:swap;src:url("/static/fonts/IBMPlexSans-Bold-latin-ext.woff2") format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF;}
@font-face{font-family:"IBM Plex Sans";font-weight:700;font-display:swap;src:url("/static/fonts/IBMPlexSans-Bold-latin.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+20AC,U+2122;}
@font-face{font-family:"IBM Plex Mono";font-weight:400;font-display:swap;src:url("/static/fonts/IBMPlexMono-Regular-latin-ext.woff2") format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF;}
@font-face{font-family:"IBM Plex Mono";font-weight:400;font-display:swap;src:url("/static/fonts/IBMPlexMono-Regular-latin.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+20AC,U+2122;}
@font-face{font-family:"IBM Plex Mono";font-weight:500;font-display:swap;src:url("/static/fonts/IBMPlexMono-Regular-latin.woff2") format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+20AC,U+2122;}
/* US-001/US-008 (PRD 5.16): IBM Plex eliminat complet — stive font sistem standard web.
Tokenurile --font-ui / --font-mono definite in :root (sursa unica de adevar). */
:root{--font-ui:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;--font-mono:ui-monospace,"SF Mono","Cascadia Code","Segoe UI Mono","Roboto Mono",Menlo,Consolas,monospace;}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{font-family:'IBM Plex Sans',system-ui,sans-serif;-webkit-font-smoothing:antialiased;background:var(--bg,#0f1218);color:var(--text,#e6e9ef);}
body{font-family:var(--font-ui);-webkit-font-smoothing:antialiased;background:var(--bg,#0f1218);color:var(--text,#e6e9ef);}
body[data-theme="grafit"]{--bg:#0f1218;--card:#181c24;--card2:#0f1218;--text:#e6e9ef;--sub:#8b93a7;--line:#262b36;--line2:#1f2530;--accent:#2E74D6;--hbg:rgba(15,18,24,.88);--okt:#2FBF8F;--infot:#6ea2ec;--errt:#E05D5D;--mut:#5c6473}
body[data-theme="cobalt"]{--bg:#080d1c;--card:#111a33;--card2:#0b1226;--text:#e9ecfb;--sub:#8a93b8;--line:#1d2747;--line2:#161f3a;--accent:#4068FF;--hbg:rgba(8,13,28,.9);--okt:#2fd0a6;--infot:#8aa0ff;--errt:#f06a7a;--mut:#5a6390}
body[data-theme="cupru"]{--bg:#15110b;--card:#211a12;--card2:#15110b;--text:#efe6d6;--sub:#a89a85;--line:#36291c;--line2:#281e14;--accent:#D98A3D;--hbg:rgba(21,17,11,.9);--okt:#67b98c;--infot:#dfa45c;--errt:#e2685a;--mut:#6d5f4c}
@@ -53,7 +47,7 @@
.lp-hactions button{height:38px!important;padding:0 11px!important;font-size:13px!important;}
}
@media (max-width:430px){
.lp-hactions [data-act="auth"][data-tab="login"]{display:none!important;}
.lp-hactions a.auth-login-link{display:none!important;}
}
</style>
</head>
@@ -61,8 +55,8 @@
<script>try{var _t=localStorage.getItem('lp-theme');if(_t&&['grafit','cobalt','cupru','hartie'].indexOf(_t)>=0)document.body.setAttribute('data-theme',_t);}catch(e){}</script>
<main class="page">
<!-- ANNOUNCE BAR -->
<div style="display:flex;align-items:center;justify-content:center;gap:16px;padding:10px 40px;background:var(--card,#181c24);border-bottom:1px solid var(--line,#262b36);font:500 13px 'IBM Plex Sans';color:var(--text,#e6e9ef);flex-wrap:wrap;">
<span style="display:inline-flex;align-items:center;gap:8px;"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Vrei să testezi sau ai un service mic? Este <strong style="font-weight:700;color:#1F9D5C;">gratuit</strong> — până la 100 de prestații/lună, fără card bancar.</span>
<div style="display:flex;align-items:center;justify-content:center;gap:16px;padding:10px 40px;background:var(--card,#181c24);border-bottom:1px solid var(--line,#262b36);font:500 13px var(--font-ui);color:var(--text,#e6e9ef);flex-wrap:wrap;">
<span style="display:inline-flex;align-items:center;gap:8px;"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Vrei să testezi sau ai un service mic? Este <strong style="font-weight:700;color:#1F9D5C;">gratuit</strong> — până la 60 de prestații/lună, fără card bancar.</span>
<a data-act="auth" data-tab="register" style="display:inline-flex;align-items:center;gap:5px;color:var(--accent,#2E74D6);font-weight:700;cursor:pointer;text-decoration:none;transition:color .18s ease, transform .18s ease;" style-hover="color:#17a96e;transform:translateX(2px)">Creează cont în 2 minute <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M13 6l6 6-6 6"/></svg></a>
</div>
@@ -70,34 +64,34 @@
<div class="lp-header" style="position:sticky;top:0;display:flex;align-items:center;justify-content:space-between;padding:0 40px;height:68px;background:var(--hbg,rgba(15,18,24,.88));backdrop-filter:blur(8px);border-bottom:1px solid var(--line,#262b36);z-index:5;">
<div style="display:flex;align-items:center;gap:48px;">
<img src="/static/romfast_logo.png" alt="ROMFAST" style="height:38px;width:auto;display:block;" />
<div class="lp-nav" style="display:flex;gap:28px;font:500 14px 'IBM Plex Sans';color:var(--sub,#8b93a7);">
<div class="lp-nav" style="display:flex;gap:28px;font:500 14px var(--font-ui);color:var(--sub,#8b93a7);">
<span>Cum funcționează</span><span>API</span><span>Preț</span>
</div>
</div>
<div class="lp-hactions" style="display:flex;align-items:center;gap:12px;">
<button data-act="theme" style="display:flex;align-items:center;gap:8px;height:40px;padding:0 13px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--sub,#8b93a7);font:500 13px 'IBM Plex Sans';cursor:pointer;">
<button data-act="theme" style="display:flex;align-items:center;gap:8px;height:40px;padding:0 13px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--sub,#8b93a7);font:500 13px var(--font-ui);cursor:pointer;">
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="9"/><path d="M12 3a9 9 0 0 0 0 18z" fill="currentColor" stroke="none"/></svg>
<span id="theme-label">Grafit</span>
</button>
<button data-act="auth" data-tab="login" style="height:44px;padding:0 18px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px 'IBM Plex Sans';cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Autentificare</button>
<button data-act="auth" data-tab="register" style="height:44px;padding:0 18px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 14px 'IBM Plex Sans';cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont</button>
<a href="/login" class="auth-login-link" style="display:inline-flex;align-items:center;height:44px;padding:0 18px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px var(--font-ui);cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;text-decoration:none;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Autentificare</a>
<button data-act="auth" data-tab="register" style="height:44px;padding:0 18px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 14px var(--font-ui);cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont</button>
</div>
</div>
<!-- HERO -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center;padding:80px 40px 72px;">
<div>
<div style="display:inline-flex;align-items:center;gap:8px;padding:7px 14px;border-radius:99px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 13px 'IBM Plex Sans';margin-bottom:24px;">
<div style="display:inline-flex;align-items:center;gap:8px;padding:7px 14px;border-radius:99px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 13px var(--font-ui);margin-bottom:24px;">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#1F9D5C" stroke-width="2.6" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>
<span><strong style="font-weight:700;color:#1F9D5C;">Gratuit</strong> pentru testare și service-uri mici · 100 prestații/lună</span>
<span><strong style="font-weight:700;color:#1F9D5C;">Gratuit</strong> pentru testare și service-uri mici · 60 prestații/lună</span>
</div>
<h1 class="lp-h1" style="font:700 50px/1.06 'IBM Plex Sans';letter-spacing:-.025em;margin:0 0 20px;color:var(--text,#e6e9ef);">Declară prestațiile la RAR AUTOPASS, automat</h1>
<p style="font:400 17px/1.6 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0 0 32px;max-width:480px;">Conformitate RAR fără bătaie de cap. Încarci un fișier sau conectezi softul de service — noi trimitem prezentările la RAR în siguranță, conform Legii 142/2023.</p>
<h1 class="lp-h1" style="font:700 50px/1.06 var(--font-ui);letter-spacing:-.025em;margin:0 0 20px;color:var(--text,#e6e9ef);">Declară prestațiile la RAR AUTOPASS, automat</h1>
<p style="font:400 17px/1.6 var(--font-ui);color:var(--sub,#8b93a7);margin:0 0 32px;max-width:480px;">Conformitate RAR fără bătaie de cap. Încarci un fișier sau conectezi softul de service — noi trimitem prezentările la RAR în siguranță, conform Legii 142/2023.</p>
<div style="display:flex;gap:12px;margin-bottom:22px;">
<button data-act="auth" data-tab="register" style="height:50px;padding:0 26px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px 'IBM Plex Sans';cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont gratuit</button>
<button style="height:50px;padding:0 24px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 15px 'IBM Plex Sans';cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Vezi cum funcționează</button>
<button data-act="auth" data-tab="register" style="height:50px;padding:0 26px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px var(--font-ui);cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont gratuit</button>
<button style="height:50px;padding:0 24px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 15px var(--font-ui);cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Vezi cum funcționează</button>
</div>
<div style="display:flex;align-items:center;gap:14px;font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);flex-wrap:wrap;">
<div style="display:flex;align-items:center;gap:14px;font:400 13px var(--font-ui);color:var(--sub,#8b93a7);flex-wrap:wrap;">
<span style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>Conform Legii 142/2023</span>
<span style="color:var(--line,#262b36);">·</span>
<span style="display:flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="1.7"><rect x="5" y="11" width="14" height="9" rx="1.5"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></svg>Datele tale criptate</span>
@@ -110,34 +104,34 @@
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:12px;box-shadow:0 24px 60px -20px rgba(0,0,0,.6);overflow:hidden;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid var(--line,#262b36);">
<div>
<div style="font:700 14px 'IBM Plex Sans';color:var(--text,#e6e9ef);">Trimiteri RAR AUTOPASS</div>
<div style="font:400 12px 'IBM Plex Mono';color:var(--sub,#8b93a7);margin-top:2px;">Service Auto Vâlcea · 28 iun 2026</div>
<div style="font:700 14px var(--font-ui);color:var(--text,#e6e9ef);">Trimiteri RAR AUTOPASS</div>
<div style="font:400 12px var(--font-mono);color:var(--sub,#8b93a7);margin-top:2px;">Service Auto Vâlcea · 28 iun 2026</div>
</div>
<div style="display:flex;gap:8px;">
<div style="display:flex;align-items:center;gap:5px;padding:4px 9px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 13%,transparent);font:500 11px 'IBM Plex Sans';color:var(--okt,#2FBF8F);"><span style="width:6px;height:6px;border-radius:99px;background:#2FBF8F;"></span>Live</div>
<div style="display:flex;align-items:center;gap:5px;padding:4px 9px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 13%,transparent);font:500 11px var(--font-ui);color:var(--okt,#2FBF8F);"><span style="width:6px;height:6px;border-radius:99px;background:#2FBF8F;"></span>Live</div>
</div>
</div>
<div style="display:flex;gap:10px;padding:14px 18px;border-bottom:1px solid var(--line,#262b36);">
<div style="flex:1;background:var(--card2,#0f1218);border:1px solid var(--line,#262b36);border-radius:8px;padding:10px 12px;"><div style="font:700 20px 'IBM Plex Sans';color:var(--text,#e6e9ef);">847</div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Trimise luna asta</div></div>
<div style="flex:1;background:var(--card2,#0f1218);border:1px solid var(--line,#262b36);border-radius:8px;padding:10px 12px;"><div style="font:700 20px 'IBM Plex Sans';color:var(--accent,#2E74D6);">12</div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);">În coadă</div></div>
<div style="flex:1;background:var(--card2,#0f1218);border:1px solid var(--line,#262b36);border-radius:8px;padding:10px 12px;"><div style="font:700 20px 'IBM Plex Sans';color:#E05D5D;">2</div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);">De corectat</div></div>
<div style="flex:1;background:var(--card2,#0f1218);border:1px solid var(--line,#262b36);border-radius:8px;padding:10px 12px;"><div style="font:700 20px var(--font-ui);color:var(--text,#e6e9ef);">847</div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);">Trimise luna asta</div></div>
<div style="flex:1;background:var(--card2,#0f1218);border:1px solid var(--line,#262b36);border-radius:8px;padding:10px 12px;"><div style="font:700 20px var(--font-ui);color:var(--accent,#2E74D6);">12</div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);">În coadă</div></div>
<div style="flex:1;background:var(--card2,#0f1218);border:1px solid var(--line,#262b36);border-radius:8px;padding:10px 12px;"><div style="font:700 20px var(--font-ui);color:#E05D5D;">2</div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);">De corectat</div></div>
</div>
<div style="padding:6px 0;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:11px 18px;border-bottom:1px solid var(--line2,#1f2530);">
<div><div style="font:500 13px 'IBM Plex Mono';color:var(--text,#e6e9ef);">WBA8E9...K7F2</div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Inspecție tehnică · 09:42</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 13%,transparent);font:500 12px 'IBM Plex Sans';color:var(--okt,#2FBF8F);"><span style="width:6px;height:6px;border-radius:99px;background:#2FBF8F;"></span>Trimis</div>
<div><div style="font:500 13px var(--font-mono);color:var(--text,#e6e9ef);">WBA8E9...K7F2</div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);">Inspecție tehnică · 09:42</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 13%,transparent);font:500 12px var(--font-ui);color:var(--okt,#2FBF8F);"><span style="width:6px;height:6px;border-radius:99px;background:#2FBF8F;"></span>Trimis</div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:11px 18px;border-bottom:1px solid var(--line2,#1f2530);">
<div><div style="font:500 13px 'IBM Plex Mono';color:var(--text,#e6e9ef);">WVWZZZ...3M1</div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Revizie periodică · 09:38</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,var(--accent,#2E74D6) 14%,transparent);font:500 12px 'IBM Plex Sans';color:var(--infot,#6ea2ec);"><span style="width:6px;height:6px;border-radius:99px;background:var(--accent,#2E74D6);"></span>În coadă</div>
<div><div style="font:500 13px var(--font-mono);color:var(--text,#e6e9ef);">WVWZZZ...3M1</div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);">Revizie periodică · 09:38</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,var(--accent,#2E74D6) 14%,transparent);font:500 12px var(--font-ui);color:var(--infot,#6ea2ec);"><span style="width:6px;height:6px;border-radius:99px;background:var(--accent,#2E74D6);"></span>În coadă</div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:11px 18px;border-bottom:1px solid var(--line2,#1f2530);">
<div><div style="font:500 13px 'IBM Plex Mono';color:var(--text,#e6e9ef);">VF1RFB...A88</div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Sistem frânare · 09:31</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#E05D5D 14%,transparent);font:500 12px 'IBM Plex Sans';color:var(--errt,#E05D5D);"><span style="width:6px;height:6px;border-radius:99px;background:#E05D5D;"></span>Eroare VIN</div>
<div><div style="font:500 13px var(--font-mono);color:var(--text,#e6e9ef);">VF1RFB...A88</div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);">Sistem frânare · 09:31</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#E05D5D 14%,transparent);font:500 12px var(--font-ui);color:var(--errt,#E05D5D);"><span style="width:6px;height:6px;border-radius:99px;background:#E05D5D;"></span>Eroare VIN</div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:11px 18px;">
<div><div style="font:500 13px 'IBM Plex Mono';color:var(--text,#e6e9ef);">ZAR937...C04</div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Schimb ulei · 09:24</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 13%,transparent);font:500 12px 'IBM Plex Sans';color:var(--okt,#2FBF8F);"><span style="width:6px;height:6px;border-radius:99px;background:#2FBF8F;"></span>Trimis</div>
<div><div style="font:500 13px var(--font-mono);color:var(--text,#e6e9ef);">ZAR937...C04</div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);">Schimb ulei · 09:24</div></div>
<div style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 13%,transparent);font:500 12px var(--font-ui);color:var(--okt,#2FBF8F);"><span style="width:6px;height:6px;border-radius:99px;background:#2FBF8F;"></span>Trimis</div>
</div>
</div>
</div>
@@ -147,27 +141,27 @@
<div style="padding:80px 40px;background:color-mix(in srgb,#E05D5D 6%,var(--bg,#0f1218));">
<div style="display:grid;grid-template-columns:1.05fr .95fr;gap:48px;align-items:start;margin:0 auto;">
<div>
<h2 style="font:700 38px/1.14 'IBM Plex Sans';letter-spacing:-.02em;margin:0 0 18px;color:var(--text,#e6e9ef);">Pentru fiecare comandă stai 23 minute și tastezi pe rar-autopass.ro</h2>
<p style="font:400 16px/1.65 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0 0 16px;">VIN, cod prestație, kilometraj, dată, tip operație — câmp cu câmp, comandă cu comandă. La 20 de mașini pe zi pierzi aproape o oră. În fiecare zi.</p>
<p style="font:400 16px/1.65 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0;">Iar dacă greșești o cifră din VIN, prestația e respinsă și o iei de la capăt — cu risc de amendă pentru raportare incompletă sau întârziată.</p>
<h2 style="font:700 38px/1.14 var(--font-ui);letter-spacing:-.02em;margin:0 0 18px;color:var(--text,#e6e9ef);">Pentru fiecare comandă stai 23 minute și tastezi pe rar-autopass.ro</h2>
<p style="font:400 16px/1.65 var(--font-ui);color:var(--sub,#8b93a7);margin:0 0 16px;">VIN, cod prestație, kilometraj, dată, tip operație — câmp cu câmp, comandă cu comandă. La 20 de mașini pe zi pierzi aproape o oră. În fiecare zi.</p>
<p style="font:400 16px/1.65 var(--font-ui);color:var(--sub,#8b93a7);margin:0;">Iar dacă greșești o cifră din VIN, prestația e respinsă și o iei de la capăt — cu risc de amendă pentru raportare incompletă sau întârziată.</p>
</div>
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:12px;padding:20px;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;"><span style="font:500 12px 'IBM Plex Mono';color:var(--sub,#8b93a7);">rar-autopass.ro · prestație nouă</span><span style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#E05D5D 14%,transparent);color:var(--errt,#E05D5D);font:600 12px 'IBM Plex Mono';"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2.5 2.5M9 2h6"/></svg>02:34</span></div>
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;"><span style="font:500 12px var(--font-mono);color:var(--sub,#8b93a7);">rar-autopass.ro · prestație nouă</span><span style="display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;background:color-mix(in srgb,#E05D5D 14%,transparent);color:var(--errt,#E05D5D);font:600 12px var(--font-mono);"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2.5 2.5M9 2h6"/></svg>02:34</span></div>
<div style="display:flex;flex-direction:column;gap:10px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:4px;">VIN</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px 'IBM Plex Mono';color:var(--text,#e6e9ef);overflow:hidden;white-space:nowrap;">U1234567890123456</div></div>
<div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:4px;">Confirmă Vin</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px 'IBM Plex Mono';color:var(--text,#e6e9ef);overflow:hidden;white-space:nowrap;">U1234567890123456</div></div>
<div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:4px;">VIN</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px var(--font-mono);color:var(--text,#e6e9ef);overflow:hidden;white-space:nowrap;">U1234567890123456</div></div>
<div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:4px;">Confirmă Vin</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px var(--font-mono);color:var(--text,#e6e9ef);overflow:hidden;white-space:nowrap;">U1234567890123456</div></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:4px;">Data prestației</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px 'IBM Plex Mono';color:var(--text,#e6e9ef);">2026-06-22</div></div>
<div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:4px;">Număr Înmatriculare</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px 'IBM Plex Mono';color:var(--text,#e6e9ef);">CT88NOE</div></div>
<div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:4px;">Data prestației</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px var(--font-mono);color:var(--text,#e6e9ef);">2026-06-22</div></div>
<div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:4px;">Număr Înmatriculare</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px var(--font-mono);color:var(--text,#e6e9ef);">CT88NOE</div></div>
</div>
<div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:4px;">Observații</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px 'IBM Plex Sans';color:var(--text,#e6e9ef);overflow:hidden;white-space:nowrap;">REVIZIE; SCHIMBARE PLĂCUȚE FRÂNĂ</div></div>
<div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:4px;">Prestații</div><div style="min-height:30px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:3px 6px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);"><span style="display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:5px;background:color-mix(in srgb,var(--accent,#2E74D6) 18%,transparent);color:var(--accent,#2E74D6);font:500 10px 'IBM Plex Sans';"><span style="opacity:.7;">×</span>REVIZIE PERIODICĂ</span><span style="display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:5px;background:color-mix(in srgb,var(--accent,#2E74D6) 18%,transparent);color:var(--accent,#2E74D6);font:500 10px 'IBM Plex Sans';"><span style="opacity:.7;">×</span>ÎNTREȚINERE</span></div></div>
<div><div style="font:400 11px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:4px;">Valoarea citită a odometrului</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px 'IBM Plex Mono';color:var(--text,#e6e9ef);">39000</div></div>
<div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:4px;">Observații</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px var(--font-ui);color:var(--text,#e6e9ef);overflow:hidden;white-space:nowrap;">REVIZIE; SCHIMBARE PLĂCUȚE FRÂNĂ</div></div>
<div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:4px;">Prestații</div><div style="min-height:30px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:3px 6px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);"><span style="display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:5px;background:color-mix(in srgb,var(--accent,#2E74D6) 18%,transparent);color:var(--accent,#2E74D6);font:500 10px var(--font-ui);"><span style="opacity:.7;">×</span>REVIZIE PERIODICĂ</span><span style="display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:5px;background:color-mix(in srgb,var(--accent,#2E74D6) 18%,transparent);color:var(--accent,#2E74D6);font:500 10px var(--font-ui);"><span style="opacity:.7;">×</span>ÎNTREȚINERE</span></div></div>
<div><div style="font:400 11px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:4px;">Valoarea citită a odometrului</div><div style="height:30px;display:flex;align-items:center;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);font:400 12px var(--font-mono);color:var(--text,#e6e9ef);">39000</div></div>
</div>
<button style="margin-top:14px;height:34px;padding:0 14px;border-radius:6px;background:color-mix(in srgb,var(--accent,#2E74D6) 40%,var(--card2,#0f1218));border:none;color:#fff;opacity:.55;font:600 12px 'IBM Plex Sans';cursor:not-allowed;align-self:flex-start;">Salvează Prezentarea</button>
<div style="margin-top:12px;font:400 12px 'IBM Plex Sans';color:var(--sub,#8b93a7);text-align:center;">se repetă pentru fiecare comandă · zi de zi</div>
<button style="margin-top:14px;height:34px;padding:0 14px;border-radius:6px;background:color-mix(in srgb,var(--accent,#2E74D6) 40%,var(--card2,#0f1218));border:none;color:#fff;opacity:.55;font:600 12px var(--font-ui);cursor:not-allowed;align-self:flex-start;">Salvează Prezentarea</button>
<div style="margin-top:12px;font:400 12px var(--font-ui);color:var(--sub,#8b93a7);text-align:center;">se repetă pentru fiecare comandă · zi de zi</div>
</div>
</div>
</div>
@@ -175,37 +169,37 @@
<!-- AGITATE / CALCULATOR -->
<div style="padding:80px 40px;">
<div style="text-align:center;max-width:720px;margin:0 auto 40px;">
<div style="font:500 13px 'IBM Plex Sans';color:var(--accent,#2E74D6);letter-spacing:.1em;text-transform:uppercase;margin-bottom:14px;">Cât te costă de fapt</div>
<h2 style="font:700 36px 'IBM Plex Sans';letter-spacing:-.02em;margin:0 0 12px;color:var(--text,#e6e9ef);">Fă socoteala. Minutele acelea sunt bani.</h2>
<p style="font:400 16px/1.6 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0;">Mută cursorul la volumul service-ului tău și vezi cât timp și câți bani pleacă pe raportarea manuală.</p>
<div style="font:500 13px var(--font-ui);color:var(--accent,#2E74D6);letter-spacing:.1em;text-transform:uppercase;margin-bottom:14px;">Cât te costă de fapt</div>
<h2 style="font:700 36px var(--font-ui);letter-spacing:-.02em;margin:0 0 12px;color:var(--text,#e6e9ef);">Fă socoteala. Minutele acelea sunt bani.</h2>
<p style="font:400 16px/1.6 var(--font-ui);color:var(--sub,#8b93a7);margin:0;">Mută cursorul la volumul service-ului tău și vezi cât timp și câți bani pleacă pe raportarea manuală.</p>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:0 auto;align-items:stretch;">
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:12px;padding:32px;">
<div style="margin-bottom:28px;">
<div style="display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;"><span style="font:500 14px 'IBM Plex Sans';color:var(--text,#e6e9ef);">Prestații pe lună</span><span style="font:700 20px 'IBM Plex Mono';color:var(--accent,#2E74D6);" id="out-pres">300</span></div>
<div style="display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;"><span style="font:500 14px var(--font-ui);color:var(--text,#e6e9ef);">Prestații pe lună</span><span style="font:700 20px var(--font-mono);color:var(--accent,#2E74D6);" id="out-pres">300</span></div>
<input type="range" min="50" max="1500" step="10" value="300" id="calc-pres" data-act="calc" style="width:100%;height:6px;accent-color:var(--accent,#2E74D6);cursor:pointer;" />
</div>
<div style="margin-bottom:24px;">
<div style="display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;"><span style="font:500 14px 'IBM Plex Sans';color:var(--text,#e6e9ef);">Cost manoperă</span><span style="font:700 20px 'IBM Plex Mono';color:var(--accent,#2E74D6);"><span id="out-rate">60</span> lei/h</span></div>
<div style="display:flex;align-items:baseline;justify-content:space-between;margin-bottom:12px;"><span style="font:500 14px var(--font-ui);color:var(--text,#e6e9ef);">Cost manoperă</span><span style="font:700 20px var(--font-mono);color:var(--accent,#2E74D6);"><span id="out-rate">60</span> lei/h</span></div>
<input type="range" min="30" max="200" step="5" value="60" id="calc-rate" data-act="calc" style="width:100%;height:6px;accent-color:var(--accent,#2E74D6);cursor:pointer;" />
</div>
<div style="display:flex;align-items:center;gap:9px;padding-top:18px;border-top:1px solid var(--line,#262b36);font:400 13px/1.5 'IBM Plex Sans';color:var(--sub,#8b93a7);"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" style="flex-shrink:0;"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2.5 2.5M9 2h6"/></svg>Estimat la ~2,5 minute de introdus manual pentru fiecare prestație.</div>
<div style="display:flex;align-items:center;gap:9px;padding-top:18px;border-top:1px solid var(--line,#262b36);font:400 13px/1.5 var(--font-ui);color:var(--sub,#8b93a7);"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" style="flex-shrink:0;"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2.5 2.5M9 2h6"/></svg>Estimat la ~2,5 minute de introdus manual pentru fiecare prestație.</div>
</div>
<div style="background:color-mix(in srgb,#E05D5D 9%,var(--card,#181c24));border:1px solid color-mix(in srgb,#E05D5D 32%,var(--line,#262b36));border-radius:12px;padding:32px;display:flex;flex-direction:column;justify-content:center;">
<div style="font:600 12px 'IBM Plex Sans';color:var(--errt,#E05D5D);letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;">Pierdut pe raportare manuală</div>
<div style="display:flex;align-items:baseline;gap:8px;"><span style="font:700 52px/1 'IBM Plex Sans';letter-spacing:-.02em;color:var(--errt,#E05D5D);"><span data-calc="leiMonth">0</span></span><span style="font:500 15px 'IBM Plex Sans';color:var(--sub,#8b93a7);">lei / lună</span></div>
<div style="font:400 14px/1.6 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-top:8px;"><span data-calc="hMonth">0</span> ore pe lună &middot; <span data-calc="leiYear">0</span> lei pe an &middot;<span data-calc="days">0</span> zile lucrătoare/an doar cu raportarea.</div>
<div style="font:600 12px var(--font-ui);color:var(--errt,#E05D5D);letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;">Pierdut pe raportare manuală</div>
<div style="display:flex;align-items:baseline;gap:8px;"><span style="font:700 52px/1 var(--font-ui);letter-spacing:-.02em;color:var(--errt,#E05D5D);"><span data-calc="leiMonth">0</span></span><span style="font:500 15px var(--font-ui);color:var(--sub,#8b93a7);">lei / lună</span></div>
<div style="font:400 14px/1.6 var(--font-ui);color:var(--sub,#8b93a7);margin-top:8px;"><span data-calc="hMonth">0</span> ore pe lună &middot; <span data-calc="leiYear">0</span> lei pe an &middot;<span data-calc="days">0</span> zile lucrătoare/an doar cu raportarea.</div>
<div style="margin-top:20px;padding-top:18px;border-top:1px solid color-mix(in srgb,#E05D5D 24%,var(--line,#262b36));">
<div style="display:flex;align-items:center;gap:9px;font:600 14px 'IBM Plex Sans';color:var(--okt,#2FBF8F);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Cu ROMFAST: câteva secunde pentru tot lotul</div>
<div style="font:400 13px/1.55 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-top:6px;">Recuperezi ~<span data-calc="leiMonth">0</span> lei/lună și timpul îl pui pe mașini, nu pe formulare.</div>
<div style="display:flex;align-items:center;gap:9px;font:600 14px var(--font-ui);color:var(--okt,#2FBF8F);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Cu ROMFAST: câteva secunde pentru tot lotul</div>
<div style="font:400 13px/1.55 var(--font-ui);color:var(--sub,#8b93a7);margin-top:6px;">Recuperezi ~<span data-calc="leiMonth">0</span> lei/lună și timpul îl pui pe mașini, nu pe formulare.</div>
</div>
</div>
</div>
<div style="margin:24px auto 0;display:flex;gap:20px;align-items:flex-start;background:color-mix(in srgb,#E0A93B 9%,var(--card,#181c24));border:1px solid color-mix(in srgb,#E0A93B 34%,var(--line,#262b36));border-radius:12px;padding:26px 28px;">
<div style="width:44px;height:44px;flex-shrink:0;border-radius:8px;background:color-mix(in srgb,#E0A93B 16%,transparent);display:flex;align-items:center;justify-content:center;color:#E0A93B;"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M12 3l8 4v5c0 4.4-3.1 8.3-8 9.5C7.1 20.3 4 16.4 4 12V7l8-4z"/><path d="M9.5 12l1.8 1.8L15 10"/></svg></div>
<div>
<div style="font:700 16px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:7px;">Evită riscul amenzilor — transmite automat la RAR Auto-Pass</div>
<p style="font:400 14px/1.65 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0;">Conform <strong style="color:var(--text,#e6e9ef);font-weight:600;">Legii nr. 142/2023</strong> și <strong style="color:var(--text,#e6e9ef);font-weight:600;">OMTI nr. 210/2024</strong>, service-urile auto autorizate RAR trebuie să transmită, la finalizarea fiecărei prestații, informațiile obligatorii (VIN, kilometraj și, după caz, date privind intervențiile asupra odometrului și reparațiile rezultate din avarii grave). Nerespectarea obligației se sancționează cu amendă între <span style="color:var(--errt,#E05D5D);font-weight:600;">2.000 și 5.000 lei</span>, iar transmiterea unor informații eronate cu amendă între <span style="color:#E0A93B;font-weight:600;">1.000 și 2.000 lei</span>.</p>
<div style="font:700 16px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:7px;">Evită riscul amenzilor — transmite automat la RAR Auto-Pass</div>
<p style="font:400 14px/1.65 var(--font-ui);color:var(--sub,#8b93a7);margin:0;">Conform <strong style="color:var(--text,#e6e9ef);font-weight:600;">Legii nr. 142/2023</strong> și <strong style="color:var(--text,#e6e9ef);font-weight:600;">OMTI nr. 210/2024</strong>, service-urile auto autorizate RAR trebuie să transmită, la finalizarea fiecărei prestații, informațiile obligatorii (VIN, kilometraj și, după caz, date privind intervențiile asupra odometrului și reparațiile rezultate din avarii grave). Nerespectarea obligației se sancționează cu amendă între <span style="color:var(--errt,#E05D5D);font-weight:600;">2.000 și 5.000 lei</span>, iar transmiterea unor informații eronate cu amendă între <span style="color:#E0A93B;font-weight:600;">1.000 și 2.000 lei</span>.</p>
</div>
</div>
</div>
@@ -213,10 +207,10 @@
<!-- SOLVE -->
<div style="padding:80px 40px;background:color-mix(in srgb,var(--accent,#2E74D6) 8%,var(--bg,#0f1218));border-top:1px solid var(--line,#262b36);border-bottom:1px solid var(--line,#262b36);">
<div style="max-width:780px;margin:0 auto;text-align:center;">
<h2 style="font:700 36px 'IBM Plex Sans';letter-spacing:-.02em;margin:0 0 18px;color:var(--text,#e6e9ef);">Nu trebuie să fii bun cu calculatorul</h2>
<p style="font:400 19px/1.75 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0 auto;max-width:660px;"><span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">Încarci</span> fișierul CSV/XLSX (sau trimiți direct prin API). ROA Auto-Pass îți propune asocierile — tu le <span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">confirmi sau corectezi</span> o singură dată — apoi le <span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">trimitem</span> la RAR, iar tu doar <span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">urmărești</span> pe ecran.</p>
<h2 style="font:700 36px var(--font-ui);letter-spacing:-.02em;margin:0 0 18px;color:var(--text,#e6e9ef);">Nu trebuie să fii bun cu calculatorul</h2>
<p style="font:400 19px/1.75 var(--font-ui);color:var(--sub,#8b93a7);margin:0 auto;max-width:660px;"><span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">Încarci</span> fișierul CSV/XLSX (sau trimiți direct prin API). ROA Auto-Pass îți propune asocierile — tu le <span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">confirmi sau corectezi</span> o singură dată — apoi le <span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">trimitem</span> la RAR, iar tu doar <span style="color:var(--accent,#2E74D6);font-weight:700;text-decoration:underline;text-underline-offset:3px;">urmărești</span> pe ecran.</p>
</div>
<div style="text-align:center;max-width:880px;margin:38px auto 0;font:400 20px/1.6 'IBM Plex Sans';color:var(--sub,#8b93a7);">
<div style="text-align:center;max-width:880px;margin:38px auto 0;font:400 20px/1.6 var(--font-ui);color:var(--sub,#8b93a7);">
<span style="text-decoration:line-through;text-decoration-color:var(--errt,#E05D5D);text-decoration-thickness:2px;">23 minute de tastat pentru fiecare comandă</span><span style="color:var(--text,#e6e9ef);font-weight:700;">&nbsp;&nbsp; câteva secunde pentru tot lotul.</span>
</div>
</div>
@@ -225,17 +219,17 @@
<div style="padding:0 40px 80px;">
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:14px;display:grid;grid-template-columns:1fr 1fr;gap:40px;padding:44px;align-items:center;">
<div>
<div style="display:inline-flex;align-items:center;gap:8px;padding:5px 11px;border-radius:99px;border:1px solid var(--line,#262b36);color:var(--sub,#8b93a7);font:500 12px 'IBM Plex Sans';margin-bottom:18px;">Pentru service-uri cu soft propriu · ROAAUTO</div>
<h2 style="font:700 30px/1.15 'IBM Plex Sans';letter-spacing:-.02em;margin:0 0 14px;color:var(--text,#e6e9ef);">Ai deja un soft de service? Conectează-l automat</h2>
<p style="font:400 15px/1.65 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0 0 24px;">Softul tău se poate conecta și direct la API-ul RAR Auto-Pass. Cu ROMFAST primești în plus asistență la maparea automată a operațiilor tale (prin mai multe metode) și salvarea mapărilor pentru trimiterile viitoare — totul printr-un singur apel, cu cheie API per cont.</p>
<button style="height:44px;padding:0 20px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px 'IBM Plex Sans';cursor:pointer;display:inline-flex;align-items:center;gap:8px;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Vezi documentația API <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
<div style="display:inline-flex;align-items:center;gap:8px;padding:5px 11px;border-radius:99px;border:1px solid var(--line,#262b36);color:var(--sub,#8b93a7);font:500 12px var(--font-ui);margin-bottom:18px;">Pentru service-uri cu soft propriu · ROAAUTO</div>
<h2 style="font:700 30px/1.15 var(--font-ui);letter-spacing:-.02em;margin:0 0 14px;color:var(--text,#e6e9ef);">Ai deja un soft de service? Conectează-l automat</h2>
<p style="font:400 15px/1.65 var(--font-ui);color:var(--sub,#8b93a7);margin:0 0 24px;">Softul tău se poate conecta și direct la API-ul RAR Auto-Pass. Cu ROMFAST primești în plus asistență la maparea automată a operațiilor tale (prin mai multe metode) și salvarea mapărilor pentru trimiterile viitoare — totul printr-un singur apel, cu cheie API per cont.</p>
<button style="height:44px;padding:0 20px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px var(--font-ui);cursor:pointer;display:inline-flex;align-items:center;gap:8px;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Vezi documentația API <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
</div>
<div style="background:#0d1015;border:1px solid #262b36;border-radius:10px;overflow:hidden;">
<div style="display:flex;align-items:center;gap:7px;padding:11px 14px;border-bottom:1px solid #262b36;">
<span style="width:11px;height:11px;border-radius:99px;background:#E05D5D;"></span><span style="width:11px;height:11px;border-radius:99px;background:#E0A93B;"></span><span style="width:11px;height:11px;border-radius:99px;background:#2FBF8F;"></span>
<span style="font:400 12px 'IBM Plex Mono';color:#8b93a7;margin-left:8px;">request.sh</span>
<span style="font:400 12px var(--font-mono);color:#8b93a7;margin-left:8px;">request.sh</span>
</div>
<pre style="margin:0;padding:18px;font:400 13px/1.7 'IBM Plex Mono';color:#e6e9ef;overflow-x:auto;"><span style="color:#2FBF8F;">POST</span> /v1/prezentari
<pre style="margin:0;padding:18px;font:400 13px/1.7 var(--font-mono);color:#e6e9ef;overflow-x:auto;"><span style="color:#2FBF8F;">POST</span> /v1/prezentari
<span style="color:#8b93a7;">Authorization:</span> <span style="color:#E0A93B;">rfak_••••••••</span>
<span style="color:#8b93a7;">Content-Type:</span> application/json
@@ -251,69 +245,69 @@
<!-- PRICING -->
<div style="padding:0 40px 80px;">
<div style="text-align:center;margin-bottom:44px;">
<div style="font:500 13px 'IBM Plex Sans';color:var(--accent,#2E74D6);letter-spacing:.08em;text-transform:uppercase;margin-bottom:12px;">Preț</div>
<h2 style="font:700 34px 'IBM Plex Sans';letter-spacing:-.02em;margin:0 0 10px;color:var(--text,#e6e9ef);">Pentru un service mic, nu costă nimic</h2>
<p style="font:400 15px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0;">Fiecare cont începe cu <strong style="color:var(--text,#e6e9ef);font-weight:600;">Premium gratuit 30 de zile</strong>. Apoi trece automat pe Gratuit — fără plată, dacă nu alegi alt plan. Fără card bancar.</p>
<div style="font:500 13px var(--font-ui);color:var(--accent,#2E74D6);letter-spacing:.08em;text-transform:uppercase;margin-bottom:12px;">Preț</div>
<h2 style="font:700 34px var(--font-ui);letter-spacing:-.02em;margin:0 0 10px;color:var(--text,#e6e9ef);">Pentru un service mic, nu costă nimic</h2>
<p style="font:400 15px var(--font-ui);color:var(--sub,#8b93a7);margin:0;">Fiecare cont începe cu <strong style="color:var(--text,#e6e9ef);font-weight:600;">Pro gratuit 30 de zile</strong>. Apoi trece automat pe Gratuit — fără plată, dacă nu alegi alt plan. Fără card bancar.</p>
</div>
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin:0 auto;align-items:start;">
<!-- Gratuit -->
<div style="background:var(--card,#181c24);border:1.5px solid var(--accent,#2E74D6);border-radius:12px;padding:26px 24px;position:relative;">
<div style="position:absolute;top:-12px;left:20px;padding:4px 11px;border-radius:99px;background:var(--accent,#2E74D6);color:#fff;font:700 10px 'IBM Plex Sans';letter-spacing:.04em;text-transform:uppercase;">Testare și firme mici</div>
<div style="font:700 17px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:8px;">Gratuit</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 32px 'IBM Plex Sans';letter-spacing:-.02em;color:var(--text,#e6e9ef);">0 lei</span><span style="font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">/lună</span></div>
<div style="font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:20px;">Fără card bancar</div>
<div style="position:absolute;top:-12px;left:20px;padding:4px 11px;border-radius:99px;background:var(--accent,#2E74D6);color:#fff;font:700 10px var(--font-ui);letter-spacing:.04em;text-transform:uppercase;">Testare și firme mici</div>
<div style="font:700 17px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:8px;">Gratuit</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 32px var(--font-ui);letter-spacing:-.02em;color:var(--text,#e6e9ef);">0 lei</span><span style="font:400 13px var(--font-ui);color:var(--sub,#8b93a7);">/lună</span></div>
<div style="font:400 13px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:20px;">Fără card bancar</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:22px;">
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Până la 100 de prestații/lună</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Sugestii automate de prestații RAR (din mii)</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Mapare manuală coloane, cu salvare</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Validare și anti-duplicat</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport contact/email în 48h</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--sub,#8b93a7);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--sub,#8b93a7)" stroke-width="1.8" style="flex-shrink:0;margin-top:1px;"><path d="M4 12h16"/></svg>Fără import API</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Până la 60 de prestații/lună</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Sugestii automate de prestații RAR (din mii)</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Mapare manuală coloane, cu salvare</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Validare și anti-duplicat</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport contact/email în 48h</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--sub,#8b93a7);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--sub,#8b93a7)" stroke-width="1.8" style="flex-shrink:0;margin-top:1px;"><path d="M4 12h16"/></svg>Fără import API</div>
<span style="display:none;"></span>
</div>
<button data-act="auth" data-tab="register" data-plan="Gratuit" style="width:100%;height:46px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 14px 'IBM Plex Sans';cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont gratuit</button>
<button data-act="auth" data-tab="register" data-plan="Gratuit" style="width:100%;height:46px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 14px var(--font-ui);cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont gratuit</button>
</div>
<!-- Standard -->
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:12px;padding:26px 24px;">
<div style="font:700 17px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:8px;">Standard</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 32px 'IBM Plex Sans';letter-spacing:-.02em;color:var(--text,#e6e9ef);">39 lei</span><span style="font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">/lună</span></div>
<div style="font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:20px;">Volum nelimitat, fără API</div>
<div style="font:700 17px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:8px;">Standard</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 32px var(--font-ui);letter-spacing:-.02em;color:var(--text,#e6e9ef);">39 lei</span><span style="font:400 13px var(--font-ui);color:var(--sub,#8b93a7);">/lună</span></div>
<div style="font:400 13px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:20px;">Volum nelimitat, fără API</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:22px;">
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Tot din Gratuit</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Prestații nelimitate</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport contact/email în 48h</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--sub,#8b93a7);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--sub,#8b93a7)" stroke-width="1.8" style="flex-shrink:0;margin-top:1px;"><path d="M4 12h16"/></svg>Fără import API</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Tot din Gratuit</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Prestații nelimitate</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport contact/email în 48h</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--sub,#8b93a7);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--sub,#8b93a7)" stroke-width="1.8" style="flex-shrink:0;margin-top:1px;"><path d="M4 12h16"/></svg>Fără import API</div>
<span style="display:none;"></span>
</div>
<button style="width:100%;height:46px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px 'IBM Plex Sans';cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)" data-act="auth" data-tab="register" data-plan="Standard">Creează cont gratuit</button>
<button style="width:100%;height:46px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px var(--font-ui);cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)" data-act="auth" data-tab="register" data-plan="Standard">Creează cont gratuit</button>
</div>
<!-- Pro -->
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:12px;padding:26px 24px;position:relative;">
<div style="position:absolute;top:-12px;left:20px;padding:4px 11px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 90%,#000);color:#fff;font:700 10px 'IBM Plex Sans';letter-spacing:.04em;text-transform:uppercase;">Cel mai ales</div>
<div style="font:700 17px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:8px;">Pro</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 32px 'IBM Plex Sans';letter-spacing:-.02em;color:var(--text,#e6e9ef);">59 lei</span><span style="font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">/lună</span></div>
<div style="font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:20px;">Cu acces API</div>
<div style="position:absolute;top:-12px;left:20px;padding:4px 11px;border-radius:99px;background:color-mix(in srgb,#2FBF8F 90%,#000);color:#fff;font:700 10px var(--font-ui);letter-spacing:.04em;text-transform:uppercase;">Cel mai ales</div>
<div style="font:700 17px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:8px;">Pro</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 32px var(--font-ui);letter-spacing:-.02em;color:var(--text,#e6e9ef);">59 lei</span><span style="font:400 13px var(--font-ui);color:var(--sub,#8b93a7);">/lună</span></div>
<div style="font:400 13px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:20px;">Cu acces API</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:22px;">
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Tot din Standard</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Import prin API + cheie API per cont</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport pe email în 24h</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Categorisire automată, cu confirmare la operațiile incerte</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Tot din Standard</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Import prin API + cheie API per cont</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport pe email în 24h</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Categorisire automată, cu confirmare la operațiile incerte</div>
<span style="display:none;"></span>
</div>
<button style="width:100%;height:46px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px 'IBM Plex Sans';cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)" data-act="auth" data-tab="register" data-plan="Pro">Creează cont gratuit</button>
<button style="width:100%;height:46px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px var(--font-ui);cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)" data-act="auth" data-tab="register" data-plan="Pro">Creează cont gratuit</button>
</div>
<!-- Premium -->
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:12px;padding:26px 24px;">
<div style="font:700 17px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:8px;">Premium</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 30px 'IBM Plex Sans';letter-spacing:-.02em;color:var(--text,#e6e9ef);">La cerere</span></div>
<div style="font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin-bottom:20px;">Soluție personalizată</div>
<div style="font:700 17px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:8px;">Premium</div>
<div style="display:flex;align-items:baseline;gap:5px;margin-bottom:4px;"><span style="font:700 30px var(--font-ui);letter-spacing:-.02em;color:var(--text,#e6e9ef);">La cerere</span></div>
<div style="font:400 13px var(--font-ui);color:var(--sub,#8b93a7);margin-bottom:20px;">Soluție personalizată</div>
<div style="display:flex;flex-direction:column;gap:10px;margin-bottom:22px;">
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Tot din Pro</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Opțiune de integrare în softul tău</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport telefonic și online</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Asistență și onboarding dedicate</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Tot din Pro</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Opțiune de integrare în softul tău</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Suport telefonic și online</div>
<div style="display:flex;gap:9px;font:400 13px/1.4 var(--font-ui);color:var(--text,#e6e9ef);"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;margin-top:1px;"><path d="M20 6L9 17l-5-5"/></svg>Asistență și onboarding dedicate</div>
</div>
<button style="width:100%;height:46px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px 'IBM Plex Sans';cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)" data-act="auth" data-tab="register" data-plan="Premium">Creează cont gratuit</button>
<button style="width:100%;height:46px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 14px var(--font-ui);cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)" data-act="auth" data-tab="register" data-plan="Premium">Creează cont gratuit</button>
</div>
</div>
</div>
@@ -321,19 +315,19 @@
<!-- PRIVACY -->
<div style="padding:80px 40px;border-top:1px solid var(--line,#262b36);">
<div style="margin:0 auto;display:grid;grid-template-columns:minmax(240px,330px) 1fr;gap:48px;align-items:center;">
<h2 style="font:700 30px/1.2 'IBM Plex Sans';letter-spacing:-.02em;margin:0;color:var(--text,#e6e9ef);">Datele clienților tăi nu devin marfă</h2>
<h2 style="font:700 30px/1.2 var(--font-ui);letter-spacing:-.02em;margin:0;color:var(--text,#e6e9ef);">Datele clienților tăi nu devin marfă</h2>
<div style="display:flex;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;padding:4px 28px;border-left:1px solid var(--line,#262b36);">
<div style="font:700 16px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:7px;">Reținem doar strict necesarul</div>
<div style="font:400 14px/1.55 'IBM Plex Sans';color:var(--sub,#8b93a7);">Doar datele de care e nevoie ca să trimitem la RAR — nimic adunat în plus, nici la conturile gratuite.</div>
<div style="font:700 16px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:7px;">Reținem doar strict necesarul</div>
<div style="font:400 14px/1.55 var(--font-ui);color:var(--sub,#8b93a7);">Doar datele de care e nevoie ca să trimitem la RAR — nimic adunat în plus, nici la conturile gratuite.</div>
</div>
<div style="flex:1;min-width:200px;padding:4px 28px;border-left:1px solid var(--line,#262b36);">
<div style="font:700 16px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:7px;">Doar pentru scopul declarat</div>
<div style="font:400 14px/1.55 'IBM Plex Sans';color:var(--sub,#8b93a7);">Maparea și trimiterea la RAR. Nu le vindem și nu le dăm mai departe.</div>
<div style="font:700 16px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:7px;">Doar pentru scopul declarat</div>
<div style="font:400 14px/1.55 var(--font-ui);color:var(--sub,#8b93a7);">Maparea și trimiterea la RAR. Nu le vindem și nu le dăm mai departe.</div>
</div>
<div style="flex:1;min-width:200px;padding:4px 28px;border-left:1px solid var(--line,#262b36);">
<div style="font:700 16px 'IBM Plex Sans';color:var(--text,#e6e9ef);margin-bottom:7px;">Se șterg la 3 luni</div>
<div style="font:400 14px/1.55 'IBM Plex Sans';color:var(--sub,#8b93a7);">Automat, fără să ceri — sau chiar acum, cu un singur click.</div>
<div style="font:700 16px var(--font-ui);color:var(--text,#e6e9ef);margin-bottom:7px;">Se șterg la 3 luni</div>
<div style="font:400 14px/1.55 var(--font-ui);color:var(--sub,#8b93a7);">Automat, fără să ceri — sau chiar acum, cu un singur click.</div>
</div>
</div>
</div>
@@ -343,39 +337,39 @@
<div id="inregistrare" style="padding:80px 40px;border-top:1px solid var(--line,#262b36);background:color-mix(in srgb,var(--accent,#2E74D6) 5%,var(--bg,#0f1218));">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:56px;margin:0 auto;align-items:center;">
<div>
<div style="font:500 13px 'IBM Plex Sans';color:var(--accent,#2E74D6);letter-spacing:.1em;text-transform:uppercase;margin-bottom:14px;">Creează cont</div>
<h2 style="font:700 34px/1.15 'IBM Plex Sans';letter-spacing:-.02em;margin:0 0 14px;color:var(--text,#e6e9ef);">Creează cont în 2 minute și declară azi la RAR</h2>
<p style="font:400 16px/1.6 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0 0 24px;">Te înregistrezi gratuit, fără card bancar. Imediat poți încărca primul fișier sau conecta softul de service.</p>
<div style="font:500 13px var(--font-ui);color:var(--accent,#2E74D6);letter-spacing:.1em;text-transform:uppercase;margin-bottom:14px;">Creează cont</div>
<h2 style="font:700 34px/1.15 var(--font-ui);letter-spacing:-.02em;margin:0 0 14px;color:var(--text,#e6e9ef);">Creează cont în 2 minute și declară azi la RAR</h2>
<p style="font:400 16px/1.6 var(--font-ui);color:var(--sub,#8b93a7);margin:0 0 24px;">Te înregistrezi gratuit, fără card bancar. Imediat poți încărca primul fișier sau conecta softul de service.</p>
<div style="display:flex;flex-direction:column;gap:12px;">
<div style="display:flex;gap:10px;align-items:center;font:400 15px 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Premium gratuit 30 de zile, apoi automat pe Gratuit</div>
<div style="display:flex;gap:10px;align-items:center;font:400 15px 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Fără card bancar la înscriere</div>
<div style="display:flex;gap:10px;align-items:center;font:400 15px 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Conform Legii 142/2023 și OMTI 210/2024</div>
<div style="display:flex;gap:10px;align-items:center;font:400 15px 'IBM Plex Sans';color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Datele cu caracter personal criptate (GDPR)</div>
<div style="display:flex;gap:10px;align-items:center;font:400 15px var(--font-ui);color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Pro gratuit 30 de zile, apoi automat pe Gratuit</div>
<div style="display:flex;gap:10px;align-items:center;font:400 15px var(--font-ui);color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Fără card bancar la înscriere</div>
<div style="display:flex;gap:10px;align-items:center;font:400 15px var(--font-ui);color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Conform Legii 142/2023 și OMTI 210/2024</div>
<div style="display:flex;gap:10px;align-items:center;font:400 15px var(--font-ui);color:var(--text,#e6e9ef);"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#2FBF8F" stroke-width="2" style="flex-shrink:0;"><path d="M20 6L9 17l-5-5"/></svg>Datele cu caracter personal criptate (GDPR)</div>
</div>
</div>
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:14px;padding:32px;box-shadow:0 20px 50px -24px rgba(0,0,0,.5);">
<div style="display:flex;gap:28px;border-bottom:1px solid var(--line,#262b36);margin-bottom:24px;">
<button type="button" data-act="tab" data-tab="register" class="auth-tab is-active" style="position:relative;background:none;border:none;padding:0 0 12px;font:700 15px 'IBM Plex Sans';color:var(--text,#e6e9ef);cursor:pointer;">Creează cont<span class="tab-underline" style="position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--accent,#2E74D6);"></span></button>
<button type="button" data-act="tab" data-tab="login" class="auth-tab" style="position:relative;background:none;border:none;padding:0 0 12px;font:700 15px 'IBM Plex Sans';color:var(--sub,#8b93a7);cursor:pointer;">Autentificare<span class="tab-underline" style="position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--accent,#2E74D6);"></span></button>
<button type="button" data-act="tab" data-tab="register" class="auth-tab is-active" style="position:relative;background:none;border:none;padding:0 0 12px;font:700 15px var(--font-ui);color:var(--text,#e6e9ef);cursor:pointer;">Creează cont<span class="tab-underline" style="position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--accent,#2E74D6);"></span></button>
<button type="button" data-act="tab" data-tab="login" class="auth-tab" style="position:relative;background:none;border:none;padding:0 0 12px;font:700 15px var(--font-ui);color:var(--sub,#8b93a7);cursor:pointer;">Autentificare<span class="tab-underline" style="position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--accent,#2E74D6);"></span></button>
</div>
<form method="post" action="/signup" data-pane="register">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Nume contact</span><input type="text" name="name" required placeholder="Ion Popescu" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px 'IBM Plex Sans';outline:none;" /></label>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">CUI</span><input type="text" name="cui" required placeholder="RO12345678" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px 'IBM Plex Mono';outline:none;" /></label>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Email</span><input type="email" name="email" required placeholder="nume@service.ro" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px 'IBM Plex Sans';outline:none;" /></label>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Parolă</span><input type="password" name="parola" required minlength="10" placeholder="Minim 10 caractere" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px 'IBM Plex Sans';outline:none;" /></label>
<label style="display:block;margin-bottom:16px;"><span style="display:block;margin-bottom:6px;font:500 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Pachet ales</span><select id="plan-select" name="plan" style="width:100%;height:44px;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px 'IBM Plex Sans';outline:none;cursor:pointer;"><option value="Gratuit" selected>Gratuit — 0 lei/lună</option><option value="Standard">Standard — 39 lei/lună</option><option value="Pro">Pro — 59 lei/lună</option><option value="Premium">Premium — la cerere</option></select></label>
<label style="display:flex;gap:9px;align-items:flex-start;margin-bottom:18px;font:400 13px/1.5 'IBM Plex Sans';color:var(--sub,#8b93a7);cursor:pointer;"><input type="checkbox" required style="margin-top:2px;accent-color:var(--accent,#2E74D6);width:16px;height:16px;flex-shrink:0;" />Sunt de acord cu Termenii și cu prelucrarea datelor conform politicii de confidențialitate (GDPR).</label>
<button type="submit" class="btn-green" style="width:100%;height:48px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px 'IBM Plex Sans';cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;">Creează cont gratuit</button>
<div style="text-align:center;margin-top:14px;font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Ai deja cont? <a data-act="tab" data-tab="login" style="color:var(--accent,#2E74D6);font-weight:500;cursor:pointer;">Autentifică-te</a></div>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px var(--font-ui);color:var(--sub,#8b93a7);">Nume contact</span><input type="text" name="name" required placeholder="Ion Popescu" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px var(--font-ui);outline:none;" /></label>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px var(--font-ui);color:var(--sub,#8b93a7);">CUI</span><input type="text" name="cui" required placeholder="RO12345678" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px var(--font-mono);outline:none;" /></label>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px var(--font-ui);color:var(--sub,#8b93a7);">Email</span><input type="email" name="email" required placeholder="nume@service.ro" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px var(--font-ui);outline:none;" /></label>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px var(--font-ui);color:var(--sub,#8b93a7);">Parolă</span><input type="password" name="parola" required minlength="10" placeholder="Minim 10 caractere" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px var(--font-ui);outline:none;" /></label>
<label style="display:block;margin-bottom:16px;"><span style="display:block;margin-bottom:6px;font:500 13px var(--font-ui);color:var(--sub,#8b93a7);">Pachet ales</span><select id="plan-select" name="plan" style="width:100%;height:44px;padding:0 10px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px var(--font-ui);outline:none;cursor:pointer;"><option value="Gratuit" selected>Gratuit — 0 lei/lună</option><option value="Standard">Standard — 39 lei/lună</option><option value="Pro">Pro — 59 lei/lună</option><option value="Premium">Premium — la cerere</option></select></label>
<label style="display:flex;gap:9px;align-items:flex-start;margin-bottom:18px;font:400 13px/1.5 var(--font-ui);color:var(--sub,#8b93a7);cursor:pointer;"><input type="checkbox" required style="margin-top:2px;accent-color:var(--accent,#2E74D6);width:16px;height:16px;flex-shrink:0;" />Sunt de acord cu Termenii și cu prelucrarea datelor conform politicii de confidențialitate (GDPR).</label>
<button type="submit" class="btn-green" style="width:100%;height:48px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px var(--font-ui);cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;">Creează cont gratuit</button>
<div style="text-align:center;margin-top:14px;font:400 13px var(--font-ui);color:var(--sub,#8b93a7);">Ai deja cont? <a data-act="tab" data-tab="login" style="color:var(--accent,#2E74D6);font-weight:500;cursor:pointer;">Autentifică-te</a></div>
</form>
<form method="post" action="/login" data-pane="login" style="display:none;">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Email</span><input type="email" name="email" required placeholder="nume@service.ro" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px 'IBM Plex Sans';outline:none;" /></label>
<label style="display:block;margin-bottom:10px;"><span style="display:block;margin-bottom:6px;font:500 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Parolă</span><input type="password" name="parola" required placeholder="Parola ta" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px 'IBM Plex Sans';outline:none;" /></label>
<div style="text-align:right;margin-bottom:18px;"><a href="/login" style="font:400 13px 'IBM Plex Sans';color:var(--accent,#2E74D6);cursor:pointer;">Ai uitat parola?</a></div>
<button type="submit" class="btn-green" style="width:100%;height:48px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px 'IBM Plex Sans';cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease;">Autentificare</button>
<div style="text-align:center;margin-top:14px;font:400 13px 'IBM Plex Sans';color:var(--sub,#8b93a7);">Nu ai cont? <a data-act="tab" data-tab="register" style="color:var(--accent,#2E74D6);font-weight:500;cursor:pointer;">Creează unul gratuit</a></div>
<label style="display:block;margin-bottom:14px;"><span style="display:block;margin-bottom:6px;font:500 13px var(--font-ui);color:var(--sub,#8b93a7);">Email</span><input type="email" name="email" required placeholder="nume@service.ro" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px var(--font-ui);outline:none;" /></label>
<label style="display:block;margin-bottom:10px;"><span style="display:block;margin-bottom:6px;font:500 13px var(--font-ui);color:var(--sub,#8b93a7);">Parolă</span><input type="password" name="parola" required placeholder="Parola ta" style="width:100%;height:44px;padding:0 12px;border:1px solid var(--line,#262b36);border-radius:6px;background:var(--card2,#0f1218);color:var(--text,#e6e9ef);font:400 14px var(--font-ui);outline:none;" /></label>
<div style="text-align:right;margin-bottom:18px;"><a href="/login" style="font:400 13px var(--font-ui);color:var(--accent,#2E74D6);cursor:pointer;">Ai uitat parola?</a></div>
<button type="submit" class="btn-green" style="width:100%;height:48px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px var(--font-ui);cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease;">Autentificare</button>
<div style="text-align:center;margin-top:14px;font:400 13px var(--font-ui);color:var(--sub,#8b93a7);">Nu ai cont? <a data-act="tab" data-tab="register" style="color:var(--accent,#2E74D6);font-weight:500;cursor:pointer;">Creează unul gratuit</a></div>
</form>
</div>
</div>
@@ -384,22 +378,22 @@
<!-- FINAL CTA -->
<div style="padding:0 40px 80px;">
<div style="background:var(--card,#181c24);border:1px solid var(--line,#262b36);border-radius:16px;padding:56px 40px;text-align:center;">
<h2 style="font:700 36px 'IBM Plex Sans';letter-spacing:-.02em;margin:0 0 14px;color:var(--text,#e6e9ef);">Începe să declari la RAR în câteva minute</h2>
<p style="font:400 16px 'IBM Plex Sans';color:var(--sub,#8b93a7);margin:0 0 28px;">Gratuit până la 100 de prezentări pe lună. Fără card bancar.</p>
<h2 style="font:700 36px var(--font-ui);letter-spacing:-.02em;margin:0 0 14px;color:var(--text,#e6e9ef);">Începe să declari la RAR în câteva minute</h2>
<p style="font:400 16px var(--font-ui);color:var(--sub,#8b93a7);margin:0 0 28px;">Gratuit până la 60 de prezentări pe lună. Fără card bancar.</p>
<div style="display:flex;gap:12px;justify-content:center;">
<button data-act="auth" data-tab="register" style="height:50px;padding:0 28px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px 'IBM Plex Sans';cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont gratuit</button>
<button data-act="auth" data-tab="login" style="height:50px;padding:0 24px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 15px 'IBM Plex Sans';cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Autentificare</button>
<button data-act="auth" data-tab="register" style="height:50px;padding:0 28px;border-radius:6px;background:#1F9D5C;border:1px solid #1F9D5C;color:#fff;font:700 15px var(--font-ui);cursor:pointer;transition:transform .2s cubic-bezier(.2,.8,.2,1), background .2s ease, box-shadow .2s ease;" style-hover="background:#16864a;border-color:#16864a;transform:translateY(-1px)">Creează cont gratuit</button>
<button data-act="auth" data-tab="login" style="height:50px;padding:0 24px;border-radius:6px;background:transparent;border:1px solid var(--line,#262b36);color:var(--text,#e6e9ef);font:500 15px var(--font-ui);cursor:pointer;transition:transform .18s ease, background .18s ease, border-color .18s ease;" style-hover="background:#1F9D5C;border-color:#1F9D5C;color:#fff;transform:translateY(-1px)">Autentificare</button>
</div>
</div>
</div>
<!-- FOOTER -->
<div style="border-top:1px solid var(--line,#262b36);padding:36px 40px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px;">
<div style="font:700 18px 'IBM Plex Sans';letter-spacing:-.02em;color:var(--text,#e6e9ef);">ROM<span style="color:var(--accent,#2E74D6);">FAST</span></div>
<div style="display:flex;gap:26px;font:400 14px 'IBM Plex Sans';color:var(--sub,#8b93a7);">
<div style="font:700 18px var(--font-ui);letter-spacing:-.02em;color:var(--text,#e6e9ef);">ROM<span style="color:var(--accent,#2E74D6);">FAST</span></div>
<div style="display:flex;gap:26px;font:400 14px var(--font-ui);color:var(--sub,#8b93a7);">
<span>Termeni</span><span>Confidențialitate / GDPR</span><span>Documentație API</span><span>Contact</span>
</div>
<div style="font:400 13px 'IBM Plex Sans';color:var(--mut,#5c6473);">© 2026 ROMFAST</div>
<div style="font:400 13px var(--font-ui);color:var(--mut,#5c6473);">© 2026 ROMFAST</div>
</div>
</main>
<script>

View File

@@ -1,28 +1,98 @@
{% extends "base.html" %}
{% block title %}Autentificare — Gateway RAR AUTOPASS{% endblock %}
{% block title %}Autentificare — ROMFAST AUTOPASS{% endblock %}
{% block content %}
<div class="card auth-card" style="max-width:400px;margin:40px auto;">
<h2 style="margin-top:0;">Autentificare</h2>
{# US-010 (PRD 5.16): /login — layout 2 coloane branduit.
Stanga: logo + tagline + puncte de incredere.
Dreapta: formular de autentificare (neschimbat: CSRF, POST /login, link signup).
Pe mobil (<640px): se stivuiesc, partea dreapta (formular) iese prima. #}
<div class="login-2col" style="max-width:860px; margin:32px auto;">
{# Antet minimal deja randat in base.html (fara RAR dot, fara burger, fara account_name) #}
<div class="login-shell">
{# === Coloana stanga: brand + trust === #}
<aside class="login-aside" aria-label="Despre ROMFAST AUTOPASS">
<div class="login-brand-row">
<img src="/static/romfast_logo.png" alt="ROMFAST" style="height:36px; width:auto;">
</div>
<h2 class="login-headline">ROMFAST <span style="color:var(--accent);">AUTOPASS</span></h2>
<p class="login-tagline">Declara prestatiile de service-auto la RAR AUTOPASS, automat.
Conform Legii 142/2023 si OMTI 210/2024.</p>
<ul class="login-trust">
<li>
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
Conform Legii 142/2023 si OMTI 210/2024
</li>
<li>
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><rect x="5" y="11" width="14" height="9" rx="1.5"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></svg>
Datele criptate, sterse la 3 luni
</li>
<li>
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
Parte din familia ROA — Romfast Applications
</li>
</ul>
</aside>
{% if error %}
<div class="banner" style="margin-bottom:12px;padding:8px 12px;">{{ error }}</div>
{% endif %}
{# === Coloana dreapta: formular (NESCHIMBAT — CSRF, POST /login, link signup) === #}
<div class="login-form-col">
<h3 style="font-size:var(--fs-xl); margin:0 0 4px;">Autentificare</h3>
<p style="font-size:var(--fs-sm); color:var(--muted); margin:0 0 22px;">
Intra in contul service-ului tau.
</p>
<form method="post" action="/login">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<p>
<label>Email</label><br>
<input type="email" name="email" required style="width:100%;">
</p>
<p>
<label>Parola</label><br>
<input type="password" name="parola" required style="width:100%;">
</p>
<button type="submit" style="width:100%;margin-top:8px;">Intra in cont</button>
</form>
{% if error %}
<div class="banner" style="margin-bottom:14px; padding:8px 12px;">{{ error }}</div>
{% endif %}
<p style="text-align:center;font-size:13px;margin-top:16px;">
Cont nou? <a href="/signup">Inregistrare</a>
</p>
<form method="post" action="/login">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="camp-slim">
<label for="lf-email">Email</label>
<input id="lf-email" type="email" name="email" required autocomplete="email">
</div>
<div class="camp-slim" style="margin-bottom:14px;">
<label for="lf-parola">Parola</label>
<input id="lf-parola" type="password" name="parola" required autocomplete="current-password">
</div>
<button type="submit" class="btn-primary-full">Intra in cont</button>
</form>
<p class="login-foot">
Cont nou? <a href="/signup" style="color:var(--accent);">Inregistreaza service-ul</a>
</p>
</div>
</div>
</div>
<style>
/* US-010 PRD 5.16: layout /login profesional 2 coloane. */
.login-shell {
display:grid; grid-template-columns:1.1fr 0.9fr;
border:1px solid var(--line); border-radius:16px; overflow:hidden;
background:var(--card); min-height:480px;
}
.login-aside {
padding:40px 38px;
background:linear-gradient(160deg, color-mix(in srgb,var(--accent) 12%,var(--card)), var(--card));
border-right:1px solid var(--line);
display:flex; flex-direction:column; justify-content:center;
}
.login-brand-row { display:flex; align-items:center; gap:10px; margin-bottom:24px; }
.login-headline { font-size:var(--fs-2xl); line-height:var(--lh-tight); margin:0 0 12px; letter-spacing:-.02em; }
.login-tagline { font-size:var(--fs-md); color:var(--muted); line-height:1.6; margin:0 0 20px; max-width:340px; }
.login-trust { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:10px; }
.login-trust li { display:flex; align-items:center; gap:9px; font-size:var(--fs-sm); color:var(--ink); }
.login-trust svg { flex-shrink:0; color:var(--ok); }
.login-form-col { padding:40px 38px; display:flex; flex-direction:column; justify-content:center; }
.btn-primary-full { width:100%; min-height:46px; font-family:var(--font-ui); font-size:var(--fs-md);
font-weight:600; background:var(--accent); color:#fff; border:none;
border-radius:8px; cursor:pointer; margin-top:4px; }
.btn-primary-full:hover { filter:brightness(1.08); }
.login-foot { text-align:center; font-size:var(--fs-sm); color:var(--muted); margin-top:18px; }
/* Mobil: stivuire verticala, formular sus */
@media (max-width:640px) {
.login-shell { grid-template-columns:1fr; grid-template-rows:auto auto; }
.login-aside { order:2; border-right:none; border-top:1px solid var(--line); padding:28px 22px; }
.login-form-col { order:1; padding:28px 22px; }
}
</style>
{% endblock %}