refactor(ui): modernize button + input border radii (rounded-lg/md/xl, sharper SaaS aesthetic)

Aligns DictIA marketing/auth/legal/billing templates with modern SaaS visual
conventions (Linear, Vercel, Stripe Dashboard, Notion). Old radii (12-18px)
felt dated; new system uses 6-12px for tighter, more contemporary corners.

Border radius system:
- Buttons (CTA, submit, secondary): rounded-[0.75rem] (12px) -> rounded-lg (8px)
- Form inputs (text/email/password/select/textarea/checkbox): rounded-[0.5rem] -> rounded-md (6px)
- Cards (pricing, bento, modals, content panels): rounded-[18px]/[14px]/[12px] -> rounded-xl (12px)
- Pricing card outer gradient frame: rounded-[20px] -> rounded-xl (matches inner)
- Pills / badges / status chips: KEEP rounded-full
- Avatars / circular icon containers: KEEP rounded-full
- Code blocks: KEEP rounded (4px)

Decision tree applied for ambiguous cases:
- Button-like clickable CTA -> rounded-lg
- Form input -> rounded-md
- Card / panel / modal -> rounded-xl
- Badge / pill / chip / avatar -> rounded-full (preserved)

In-scope templates modified (23):
- macros/button.html (central macro, cascades to all callers)
- macros/pricing_card.html, macros/bento.html
- marketing/landing.html, tarifs.html, fonctionnalites.html, conformite.html, contact.html
- auth/check_email.html, forgot_password.html, magic_link_request.html, oauth_finish_signup.html,
  passkey_setup.html, reset_password.html, totp_setup.html, totp_verify.html, verify_success.html
- billing/cancel.html, billing/success.html
- legal/_layout.html, legal/index.html
- register.html, login.html

Out of scope (untouched): index.html, account.html, admin.html, share.html, inquire.html,
group-admin.html, components/**, includes/**, modals/** (all legacy Speakr Vue surfaces).

Tests: test_marketing_landing_template.py — 2 assertions updated to match new bento icon
(rounded-md) and pricing card frame (rounded-xl). All 18 legal tests + 58 marketing landing
tests + 9 signup_loi25 tests still pass. Decorative rounded-full preserved on hero cosmic
orbs and ordres-pros avatar circles.

Diff: 94 insertions / 94 deletions (1:1 mechanical replacement, no class drift).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Allison
2026-04-28 09:55:13 -04:00
parent 924d127ab4
commit 48ff4e70e6
24 changed files with 94 additions and 94 deletions

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="check-email-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="check-email-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta text-center"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta text-center">
<div class="mx-auto mb-6 w-16 h-16 rounded-full grad-bg flex items-center justify-center text-white text-2xl" aria-hidden="true">&#x2709;</div> <div class="mx-auto mb-6 w-16 h-16 rounded-full grad-bg flex items-center justify-center text-white text-2xl" aria-hidden="true">&#x2709;</div>
<h1 id="check-email-title" class="text-2xl font-black text-brand-navy mb-2"> <h1 id="check-email-title" class="text-2xl font-black text-brand-navy mb-2">
@@ -45,7 +45,7 @@
<form method="POST" action="{{ url_for('auth.resend_verification') }}" class="mb-4"> <form method="POST" action="{{ url_for('auth.resend_verification') }}" class="mb-4">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="email" value="{{ email }}"> <input type="hidden" name="email" value="{{ email }}">
<button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Renvoyer le lien de vérification Renvoyer le lien de vérification
</button> </button>
</form> </form>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="forgot-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="forgot-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="forgot-title" class="text-3xl font-black text-brand-navy mb-2">Mot de passe oublié</h1> <h1 id="forgot-title" class="text-3xl font-black text-brand-navy mb-2">Mot de passe oublié</h1>
<p class="text-sm text-brand-navy/70 mb-6">{{ "Entrez votre adresse courriel. Si un compte existe, nous vous enverrons un lien sécurisé pour réinitialiser votre mot de passe (valide 1&nbsp;heure)." | safe }}</p> <p class="text-sm text-brand-navy/70 mb-6">{{ "Entrez votre adresse courriel. Si un compte existe, nous vous enverrons un lien sécurisé pour réinitialiser votre mot de passe (valide 1&nbsp;heure)." | safe }}</p>
@@ -29,11 +29,11 @@
<div> <div>
<label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label> <label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required aria-required="true" <input type="email" id="email" name="email" autocomplete="email" required aria-required="true"
class="w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2" class="w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
placeholder="vous@cabinet.qc.ca"> placeholder="vous@cabinet.qc.ca">
</div> </div>
<button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Recevoir un lien de réinitialisation Recevoir un lien de réinitialisation
</button> </button>
</form> </form>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="magic-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="magic-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="magic-title" class="text-3xl font-black text-brand-navy mb-2">Lien de connexion</h1> <h1 id="magic-title" class="text-3xl font-black text-brand-navy mb-2">Lien de connexion</h1>
<p class="text-sm text-brand-navy/70 mb-6">{{ "Recevez un lien par courriel pour vous connecter sans mot de passe. Le lien expire dans 15&nbsp;minutes." | safe }}</p> <p class="text-sm text-brand-navy/70 mb-6">{{ "Recevez un lien par courriel pour vous connecter sans mot de passe. Le lien expire dans 15&nbsp;minutes." | safe }}</p>
@@ -29,11 +29,11 @@
<div> <div>
<label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label> <label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required aria-required="true" <input type="email" id="email" name="email" autocomplete="email" required aria-required="true"
class="w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2" class="w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
placeholder="vous@cabinet.qc.ca"> placeholder="vous@cabinet.qc.ca">
</div> </div>
<button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
{{ "Recevoir le lien (expire dans 15&nbsp;minutes)" | safe }} {{ "Recevoir le lien (expire dans 15&nbsp;minutes)" | safe }}
</button> </button>
</form> </form>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="finish-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="finish-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="finish-title" class="text-3xl font-black text-brand-navy mb-2">Finaliser votre inscription</h1> <h1 id="finish-title" class="text-3xl font-black text-brand-navy mb-2">Finaliser votre inscription</h1>
<p class="text-sm text-brand-navy/70 mb-6"> <p class="text-sm text-brand-navy/70 mb-6">
Vous vous inscrivez via <strong>{{ provider_display or provider | capitalize }}</strong>. Avant de créer votre compte DictIA, nous devons obtenir vos consentements conformément à la {{ "Loi&nbsp;25" | safe }} du Québec. Vous vous inscrivez via <strong>{{ provider_display or provider | capitalize }}</strong>. Avant de créer votre compte DictIA, nous devons obtenir vos consentements conformément à la {{ "Loi&nbsp;25" | safe }} du Québec.
@@ -26,7 +26,7 @@
{% endwith %} {% endwith %}
{# Pre-filled email from OAuth provider — display only, not editable #} {# Pre-filled email from OAuth provider — display only, not editable #}
<div class="bg-brand-bg border border-brand-border rounded-[0.5rem] p-3 mb-6 text-sm"> <div class="bg-brand-bg border border-brand-border rounded-md p-3 mb-6 text-sm">
<p class="text-brand-navy/70 mb-1">Compte fédéré :</p> <p class="text-brand-navy/70 mb-1">Compte fédéré :</p>
<p class="text-brand-navy font-semibold break-all">{{ userinfo.email }}</p> <p class="text-brand-navy font-semibold break-all">{{ userinfo.email }}</p>
{% if userinfo.name %}<p class="text-brand-navy/80 text-xs mt-1">{{ userinfo.name }}</p>{% endif %} {% if userinfo.name %}<p class="text-brand-navy/80 text-xs mt-1">{{ userinfo.name }}</p>{% endif %}
@@ -66,7 +66,7 @@
</label> </label>
</fieldset> </fieldset>
<button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Créer mon compte DictIA Créer mon compte DictIA
</button> </button>
</form> </form>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="passkey-setup-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="passkey-setup-title">
<div class="max-w-2xl mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-2xl mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="passkey-setup-title" class="text-3xl font-black text-brand-navy mb-2">Mes passkeys</h1> <h1 id="passkey-setup-title" class="text-3xl font-black text-brand-navy mb-2">Mes passkeys</h1>
<p class="text-sm text-brand-navy/70 mb-6">{{ "Une passkey est un second facteur sans mot de passe (clé matérielle YubiKey, biométrie de votre appareil, etc.). Conforme&nbsp;Loi&nbsp;25." | safe }}</p> <p class="text-sm text-brand-navy/70 mb-6">{{ "Une passkey est un second facteur sans mot de passe (clé matérielle YubiKey, biométrie de votre appareil, etc.). Conforme&nbsp;Loi&nbsp;25." | safe }}</p>
@@ -48,8 +48,8 @@
<h2 class="text-base font-semibold text-brand-navy mb-3">Ajouter une passkey</h2> <h2 class="text-base font-semibold text-brand-navy mb-3">Ajouter une passkey</h2>
<div class="space-y-3"> <div class="space-y-3">
<label for="passkey-label" class="block text-sm font-medium text-brand-navy">Nom de la passkey (optionnel)</label> <label for="passkey-label" class="block text-sm font-medium text-brand-navy">Nom de la passkey (optionnel)</label>
<input id="passkey-label" type="text" maxlength="80" placeholder="ex. YubiKey 5C, MacBook Touch ID..." class="w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <input id="passkey-label" type="text" maxlength="80" placeholder="ex. YubiKey 5C, MacBook Touch ID..." class="w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
<button id="passkey-register-btn" type="button" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button id="passkey-register-btn" type="button" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Enregistrer une passkey Enregistrer une passkey
</button> </button>
<p id="passkey-register-status" class="text-xs text-brand-navy/70" role="status" aria-live="polite"></p> <p id="passkey-register-status" class="text-xs text-brand-navy/70" role="status" aria-live="polite"></p>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="reset-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="reset-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="reset-title" class="text-3xl font-black text-brand-navy mb-2">Nouveau mot de passe</h1> <h1 id="reset-title" class="text-3xl font-black text-brand-navy mb-2">Nouveau mot de passe</h1>
<p class="text-sm text-brand-navy/70 mb-6">Choisissez un mot de passe robuste pour sécuriser votre compte DictIA.</p> <p class="text-sm text-brand-navy/70 mb-6">Choisissez un mot de passe robuste pour sécuriser votre compte DictIA.</p>
@@ -29,7 +29,7 @@
<div> <div>
<label for="password" class="block text-sm font-medium text-brand-navy mb-1">Nouveau mot de passe <span class="text-red-600" aria-hidden="true">*</span></label> <label for="password" class="block text-sm font-medium text-brand-navy mb-1">Nouveau mot de passe <span class="text-red-600" aria-hidden="true">*</span></label>
<input type="password" id="password" name="password" autocomplete="new-password" minlength="8" required aria-required="true" aria-describedby="password-help" <input type="password" id="password" name="password" autocomplete="new-password" minlength="8" required aria-required="true" aria-describedby="password-help"
class="w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2" class="w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
placeholder="••••••••"> placeholder="••••••••">
<p id="password-help" class="text-xs text-brand-navy/70 mt-1">{{ "8&nbsp;caractères minimum, dont une majuscule, une minuscule, un chiffre et un caractère spécial." | safe }}</p> <p id="password-help" class="text-xs text-brand-navy/70 mt-1">{{ "8&nbsp;caractères minimum, dont une majuscule, une minuscule, un chiffre et un caractère spécial." | safe }}</p>
</div> </div>
@@ -37,11 +37,11 @@
<div> <div>
<label for="confirm_password" class="block text-sm font-medium text-brand-navy mb-1">Confirmer le mot de passe <span class="text-red-600" aria-hidden="true">*</span></label> <label for="confirm_password" class="block text-sm font-medium text-brand-navy mb-1">Confirmer le mot de passe <span class="text-red-600" aria-hidden="true">*</span></label>
<input type="password" id="confirm_password" name="confirm_password" autocomplete="new-password" minlength="8" required aria-required="true" <input type="password" id="confirm_password" name="confirm_password" autocomplete="new-password" minlength="8" required aria-required="true"
class="w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2" class="w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
placeholder="••••••••"> placeholder="••••••••">
</div> </div>
<button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Définir mon nouveau mot de passe Définir mon nouveau mot de passe
</button> </button>
</form> </form>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="totp-setup-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="totp-setup-title">
<div class="max-w-2xl mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-2xl mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="totp-setup-title" class="text-3xl font-black text-brand-navy mb-2">Configurer la double authentification</h1> <h1 id="totp-setup-title" class="text-3xl font-black text-brand-navy mb-2">Configurer la double authentification</h1>
<p class="text-sm text-brand-navy/70 mb-6">{{ "La double authentification (2FA) ajoute une seconde étape lors de la connexion, en plus de votre mot de passe. Une exigence forte recommandée pour les comptes traitant des données confidentielles (Loi&nbsp;25)." | safe }}</p> <p class="text-sm text-brand-navy/70 mb-6">{{ "La double authentification (2FA) ajoute une seconde étape lors de la connexion, en plus de votre mot de passe. Une exigence forte recommandée pour les comptes traitant des données confidentielles (Loi&nbsp;25)." | safe }}</p>
@@ -36,7 +36,7 @@
<li> <li>
<h2 class="text-lg font-bold text-brand-navy mb-2"><span class="grad-text">2.</span> Scannez le code QR</h2> <h2 class="text-lg font-bold text-brand-navy mb-2"><span class="grad-text">2.</span> Scannez le code QR</h2>
<div class="flex flex-col md:flex-row gap-6 items-start"> <div class="flex flex-col md:flex-row gap-6 items-start">
<div class="bg-brand-bg border border-brand-border rounded-[0.75rem] p-4 flex-shrink-0"> <div class="bg-brand-bg border border-brand-border rounded-lg p-4 flex-shrink-0">
<img src="{{ qr_data_url }}" alt="Code QR pour configurer DictIA dans votre application authenticator" class="w-48 h-48 mx-auto block"> <img src="{{ qr_data_url }}" alt="Code QR pour configurer DictIA dans votre application authenticator" class="w-48 h-48 mx-auto block">
</div> </div>
<div class="text-sm text-brand-navy/80 space-y-2"> <div class="text-sm text-brand-navy/80 space-y-2">
@@ -49,11 +49,11 @@
<li> <li>
<h2 class="text-lg font-bold text-brand-navy mb-2"><span class="grad-text">3.</span> Conservez vos codes de récupération</h2> <h2 class="text-lg font-bold text-brand-navy mb-2"><span class="grad-text">3.</span> Conservez vos codes de récupération</h2>
<div role="alert" class="bg-amber-50 border border-amber-200 rounded-[0.75rem] p-4 mb-3"> <div role="alert" class="bg-amber-50 border border-amber-200 rounded-lg p-4 mb-3">
<p class="text-sm font-semibold text-amber-900 mb-2">Important — ces codes ne seront affichés qu'une seule fois.</p> <p class="text-sm font-semibold text-amber-900 mb-2">Important — ces codes ne seront affichés qu'une seule fois.</p>
<p class="text-xs text-amber-900/90">Imprimez-les ou enregistrez-les dans votre gestionnaire de mots de passe. Chaque code est à usage unique et permettra de vous reconnecter si vous perdez l'accès à votre application authenticator.</p> <p class="text-xs text-amber-900/90">Imprimez-les ou enregistrez-les dans votre gestionnaire de mots de passe. Chaque code est à usage unique et permettra de vous reconnecter si vous perdez l'accès à votre application authenticator.</p>
</div> </div>
<pre id="recovery-codes" class="bg-brand-navy text-white text-sm font-mono p-4 rounded-[0.75rem] whitespace-pre-wrap select-all">{% for c in recovery_codes %}{{ c }} <pre id="recovery-codes" class="bg-brand-navy text-white text-sm font-mono p-4 rounded-lg whitespace-pre-wrap select-all">{% for c in recovery_codes %}{{ c }}
{% endfor %}</pre> {% endfor %}</pre>
<button type="button" onclick="(function(){var t=document.getElementById('recovery-codes').innerText;if(navigator.clipboard){navigator.clipboard.writeText(t);}var b=document.getElementById('copy-btn');b.textContent='Copié dans le presse-papiers';setTimeout(function(){b.textContent='Copier les codes';},2000);})();" <button type="button" onclick="(function(){var t=document.getElementById('recovery-codes').innerText;if(navigator.clipboard){navigator.clipboard.writeText(t);}var b=document.getElementById('copy-btn');b.textContent='Copié dans le presse-papiers';setTimeout(function(){b.textContent='Copier les codes';},2000);})();"
id="copy-btn" id="copy-btn"
@@ -74,11 +74,11 @@
<input type="text" id="code" name="code" required aria-required="true" <input type="text" id="code" name="code" required aria-required="true"
inputmode="numeric" autocomplete="one-time-code" inputmode="numeric" autocomplete="one-time-code"
pattern="[0-9]{6}" maxlength="6" pattern="[0-9]{6}" maxlength="6"
class="w-full md:w-48 px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy text-center text-xl font-mono tracking-widest focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2" class="w-full md:w-48 px-3 py-2 border border-brand-border rounded-md text-brand-navy text-center text-xl font-mono tracking-widest focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
placeholder="000000" autofocus> placeholder="000000" autofocus>
</div> </div>
<button type="submit" class="grad-bg text-white font-semibold py-3 px-6 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="grad-bg text-white font-semibold py-3 px-6 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Activer la double authentification Activer la double authentification
</button> </button>
</form> </form>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="totp-verify-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="totp-verify-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="totp-verify-title" class="text-3xl font-black text-brand-navy mb-2">Vérification en deux étapes</h1> <h1 id="totp-verify-title" class="text-3xl font-black text-brand-navy mb-2">Vérification en deux étapes</h1>
<p class="text-sm text-brand-navy/70 mb-6">Entrez le code à 6 chiffres affiché dans votre application authenticator pour terminer la connexion.</p> <p class="text-sm text-brand-navy/70 mb-6">Entrez le code à 6 chiffres affiché dans votre application authenticator pour terminer la connexion.</p>
@@ -31,7 +31,7 @@
{% if has_passkeys %} {% if has_passkeys %}
<section class="mb-6" aria-labelledby="passkey-section-title"> <section class="mb-6" aria-labelledby="passkey-section-title">
<h2 id="passkey-section-title" class="text-base font-semibold text-brand-navy mb-3">Connexion par Passkey</h2> <h2 id="passkey-section-title" class="text-base font-semibold text-brand-navy mb-3">Connexion par Passkey</h2>
<button id="passkey-auth-btn" type="button" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button id="passkey-auth-btn" type="button" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Utiliser ma Passkey Utiliser ma Passkey
</button> </button>
<p id="passkey-status" class="text-xs text-brand-navy/70 mt-2" role="status" aria-live="polite"></p> <p id="passkey-status" class="text-xs text-brand-navy/70 mt-2" role="status" aria-live="polite"></p>
@@ -54,11 +54,11 @@
<input type="text" id="code" name="code" <input type="text" id="code" name="code"
inputmode="numeric" autocomplete="one-time-code" inputmode="numeric" autocomplete="one-time-code"
pattern="[0-9]{6}" maxlength="6" pattern="[0-9]{6}" maxlength="6"
class="w-full px-3 py-3 border border-brand-border rounded-[0.5rem] text-brand-navy text-center text-2xl font-mono tracking-widest focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2" class="w-full px-3 py-3 border border-brand-border rounded-md text-brand-navy text-center text-2xl font-mono tracking-widest focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
placeholder="000000" autofocus> placeholder="000000" autofocus>
</div> </div>
<button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Vérifier et se connecter Vérifier et se connecter
</button> </button>
</form> </form>
@@ -74,11 +74,11 @@
<label for="recovery_code" class="block text-sm font-medium text-brand-navy mb-1">Code de récupération <span class="text-red-600" aria-hidden="true">*</span></label> <label for="recovery_code" class="block text-sm font-medium text-brand-navy mb-1">Code de récupération <span class="text-red-600" aria-hidden="true">*</span></label>
<input type="text" id="recovery_code" name="recovery_code" <input type="text" id="recovery_code" name="recovery_code"
autocomplete="off" autocomplete="off"
class="w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy font-mono uppercase focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2" class="w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy font-mono uppercase focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
placeholder="XXXXX-XXXXX"> placeholder="XXXXX-XXXXX">
<p class="text-xs text-brand-navy/60 mt-1">Format&nbsp;: 5 caractères + tiret + 5 caractères. Chaque code est à usage unique.</p> <p class="text-xs text-brand-navy/60 mt-1">Format&nbsp;: 5 caractères + tiret + 5 caractères. Chaque code est à usage unique.</p>
</div> </div>
<button type="submit" class="w-full bg-brand-navy text-white font-semibold py-3 rounded-[0.75rem] hover:bg-brand-navy2 transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full bg-brand-navy text-white font-semibold py-3 rounded-lg hover:bg-brand-navy2 transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Utiliser le code de récupération Utiliser le code de récupération
</button> </button>
<p class="text-xs text-brand-navy/60 text-center" aria-live="polite">{{ recovery_codes_remaining }} code{{ 's' if recovery_codes_remaining != 1 else '' }} de récupération restant{{ 's' if recovery_codes_remaining != 1 else '' }}.</p> <p class="text-xs text-brand-navy/60 text-center" aria-live="polite">{{ recovery_codes_remaining }} code{{ 's' if recovery_codes_remaining != 1 else '' }} de récupération restant{{ 's' if recovery_codes_remaining != 1 else '' }}.</p>

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="verify-success-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="verify-success-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta text-center"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta text-center">
<div class="mx-auto mb-6 w-16 h-16 rounded-full bg-green-100 text-green-700 flex items-center justify-center text-3xl font-black" aria-hidden="true">&check;</div> <div class="mx-auto mb-6 w-16 h-16 rounded-full bg-green-100 text-green-700 flex items-center justify-center text-3xl font-black" aria-hidden="true">&check;</div>
<h1 id="verify-success-title" class="text-2xl font-black text-brand-navy mb-2">Votre courriel a été vérifié</h1> <h1 id="verify-success-title" class="text-2xl font-black text-brand-navy mb-2">Votre courriel a été vérifié</h1>
@@ -14,7 +14,7 @@
</p> </p>
<a href="{{ url_for('auth.login') }}" <a href="{{ url_for('auth.login') }}"
class="inline-block w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> class="inline-block w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Se connecter Se connecter
</a> </a>

View File

@@ -24,7 +24,7 @@
<div class="max-w-[820px] mx-auto px-6"> <div class="max-w-[820px] mx-auto px-6">
<h2 id="info-title" class="sr-only">Que faire ensuite</h2> <h2 id="info-title" class="sr-only">Que faire ensuite</h2>
<div class="bg-white p-8 rounded-[18px] border border-brand-border mb-8"> <div class="bg-white p-8 rounded-xl border border-brand-border mb-8">
<h3 class="text-lg font-bold mb-3 text-brand-navy">Pourquoi avoir hésité&nbsp;?</h3> <h3 class="text-lg font-bold mb-3 text-brand-navy">Pourquoi avoir hésité&nbsp;?</h3>
<p class="text-sm text-brand-navy/80 leading-relaxed mb-4"> <p class="text-sm text-brand-navy/80 leading-relaxed mb-4">
Si vous avez une question sur les forfaits, la conformité Loi&nbsp;25 ou la mise en service, notre équipe peut vous accompagner sans pression commerciale. Si vous avez une question sur les forfaits, la conformité Loi&nbsp;25 ou la mise en service, notre équipe peut vous accompagner sans pression commerciale.

View File

@@ -27,7 +27,7 @@
</h2> </h2>
<ol class="space-y-6"> <ol class="space-y-6">
<li class="bg-white p-6 rounded-[18px] border border-brand-border flex gap-4"> <li class="bg-white p-6 rounded-xl border border-brand-border flex gap-4">
<span class="grad-bg text-white font-black w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 shadow-cta" aria-hidden="true">1</span> <span class="grad-bg text-white font-black w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 shadow-cta" aria-hidden="true">1</span>
<div> <div>
<h3 class="font-bold text-brand-navy mb-1">Confirmation par courriel</h3> <h3 class="font-bold text-brand-navy mb-1">Confirmation par courriel</h3>
@@ -37,7 +37,7 @@
</div> </div>
</li> </li>
<li class="bg-white p-6 rounded-[18px] border border-brand-border flex gap-4"> <li class="bg-white p-6 rounded-xl border border-brand-border flex gap-4">
<span class="grad-bg text-white font-black w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 shadow-cta" aria-hidden="true">2</span> <span class="grad-bg text-white font-black w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 shadow-cta" aria-hidden="true">2</span>
<div> <div>
<h3 class="font-bold text-brand-navy mb-1">Activation de votre abonnement</h3> <h3 class="font-bold text-brand-navy mb-1">Activation de votre abonnement</h3>
@@ -47,7 +47,7 @@
</div> </div>
</li> </li>
<li class="bg-white p-6 rounded-[18px] border border-brand-border flex gap-4"> <li class="bg-white p-6 rounded-xl border border-brand-border flex gap-4">
<span class="grad-bg text-white font-black w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 shadow-cta" aria-hidden="true">3</span> <span class="grad-bg text-white font-black w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 shadow-cta" aria-hidden="true">3</span>
<div> <div>
<h3 class="font-bold text-brand-navy mb-1">Mise en service</h3> <h3 class="font-bold text-brand-navy mb-1">Mise en service</h3>

View File

@@ -221,7 +221,7 @@
<article id="main-content" <article id="main-content"
role="main" role="main"
aria-labelledby="legal-title" aria-labelledby="legal-title"
class="bg-white p-6 md:p-10 rounded-[18px] border border-brand-border shadow-cta order-1"> class="bg-white p-6 md:p-10 rounded-xl border border-brand-border shadow-cta order-1">
<header class="mb-8 pb-6 border-b border-brand-border"> <header class="mb-8 pb-6 border-b border-brand-border">
<p class="text-xs uppercase tracking-wider text-brand-navy/70 mb-2">Document légal DictIA</p> <p class="text-xs uppercase tracking-wider text-brand-navy/70 mb-2">Document légal DictIA</p>
<h1 id="legal-title" class="text-3xl md:text-4xl font-black text-brand-navy mb-4 tracking-tight">{{ title }}</h1> <h1 id="legal-title" class="text-3xl md:text-4xl font-black text-brand-navy mb-4 tracking-tight">{{ title }}</h1>
@@ -244,8 +244,8 @@
</header> </header>
{# TOC mobile (collapsible) — visible < lg seulement #} {# TOC mobile (collapsible) — visible < lg seulement #}
<details class="lg:hidden mb-6 border border-brand-border rounded-[12px] bg-brand-bg/50"> <details class="lg:hidden mb-6 border border-brand-border rounded-xl bg-brand-bg/50">
<summary class="cursor-pointer px-4 py-3 text-sm font-semibold text-brand-navy flex items-center justify-between focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 rounded-[12px]"> <summary class="cursor-pointer px-4 py-3 text-sm font-semibold text-brand-navy flex items-center justify-between focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 rounded-xl">
<span>Sur cette page</span> <span>Sur cette page</span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
@@ -268,7 +268,7 @@
{% if prev_page %} {% if prev_page %}
<a href="{{ url_for('legal.legal_page', page=prev_page) }}" <a href="{{ url_for('legal.legal_page', page=prev_page) }}"
rel="prev" rel="prev"
class="block p-4 bg-brand-bg/60 border border-brand-border rounded-[12px] hover:border-brand-b1 hover:bg-white transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> class="block p-4 bg-brand-bg/60 border border-brand-border rounded-xl hover:border-brand-b1 hover:bg-white transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
<span class="block text-xs uppercase tracking-wider text-brand-navy/60 mb-1"> <span class="block text-xs uppercase tracking-wider text-brand-navy/60 mb-1">
<span aria-hidden="true"></span> Précédent <span aria-hidden="true"></span> Précédent
</span> </span>
@@ -281,7 +281,7 @@
{% if next_page %} {% if next_page %}
<a href="{{ url_for('legal.legal_page', page=next_page) }}" <a href="{{ url_for('legal.legal_page', page=next_page) }}"
rel="next" rel="next"
class="block p-4 bg-brand-bg/60 border border-brand-border rounded-[12px] hover:border-brand-b1 hover:bg-white transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 sm:text-right"> class="block p-4 bg-brand-bg/60 border border-brand-border rounded-xl hover:border-brand-b1 hover:bg-white transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 sm:text-right">
<span class="block text-xs uppercase tracking-wider text-brand-navy/60 mb-1"> <span class="block text-xs uppercase tracking-wider text-brand-navy/60 mb-1">
Suivant <span aria-hidden="true"></span> Suivant <span aria-hidden="true"></span>
</span> </span>
@@ -304,7 +304,7 @@
aria-label="Table des matières"> aria-label="Table des matières">
<div x-data="legalToc()" <div x-data="legalToc()"
x-init="init()" x-init="init()"
class="legal-toc bg-white border border-brand-border rounded-[14px] p-5 mt-0"> class="legal-toc bg-white border border-brand-border rounded-xl p-5 mt-0">
<h2 class="text-xs font-bold uppercase tracking-wider text-brand-navy/70 mb-3"> <h2 class="text-xs font-bold uppercase tracking-wider text-brand-navy/70 mb-3">
Sur cette page Sur cette page
</h2> </h2>

View File

@@ -68,10 +68,10 @@
<a href="{{ page.url }}" <a href="{{ page.url }}"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="legal-card is-external group block h-full p-5 md:p-6 bg-white border border-brand-border rounded-[14px] hover:border-brand-b1 hover:shadow-cta focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 relative"> class="legal-card is-external group block h-full p-5 md:p-6 bg-white border border-brand-border rounded-xl hover:border-brand-b1 hover:shadow-cta focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 relative">
{% else %} {% else %}
<a href="{{ url_for('legal.legal_page', page=page.slug) }}" <a href="{{ url_for('legal.legal_page', page=page.slug) }}"
class="legal-card group block h-full p-5 md:p-6 bg-white border border-brand-border rounded-[14px] hover:border-brand-b1 hover:shadow-cta focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 relative"> class="legal-card group block h-full p-5 md:p-6 bg-white border border-brand-border rounded-xl hover:border-brand-b1 hover:shadow-cta focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 relative">
{% endif %} {% endif %}
<div class="flex items-start gap-4"> <div class="flex items-start gap-4">
<div class="legal-card-icon shrink-0 inline-flex items-center justify-center w-12 h-12 rounded-xl"> <div class="legal-card-icon shrink-0 inline-flex items-center justify-center w-12 h-12 rounded-xl">
@@ -97,7 +97,7 @@
</ul> </ul>
{# Bloc info pied — signature, contact, sous-processeurs #} {# Bloc info pied — signature, contact, sous-processeurs #}
<aside class="mt-12 max-w-3xl mx-auto bg-white border border-brand-border rounded-[14px] p-6 md:p-7" <aside class="mt-12 max-w-3xl mx-auto bg-white border border-brand-border rounded-xl p-6 md:p-7"
aria-label="Informations complémentaires sur les documents légaux"> aria-label="Informations complémentaires sur les documents légaux">
<p class="text-sm text-brand-navy/80 leading-relaxed mb-3"> <p class="text-sm text-brand-navy/80 leading-relaxed mb-3">
Documents signés numériquement par <strong class="text-brand-navy">Allison Rioux</strong>, Documents signés numériquement par <strong class="text-brand-navy">Allison Rioux</strong>,

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="login-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="login-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="login-title" class="text-3xl font-black text-brand-navy mb-2">Connexion</h1> <h1 id="login-title" class="text-3xl font-black text-brand-navy mb-2">Connexion</h1>
<p class="text-sm text-brand-navy/70 mb-6">{{ "Bienvenue sur DictIA — la transcription IA conforme à la Loi&nbsp;25." | safe }}</p> <p class="text-sm text-brand-navy/70 mb-6">{{ "Bienvenue sur DictIA — la transcription IA conforme à la Loi&nbsp;25." | safe }}</p>
@@ -28,7 +28,7 @@
<div class="space-y-3 mb-6" aria-label="Connexion fédérée"> <div class="space-y-3 mb-6" aria-label="Connexion fédérée">
{% if oauth_microsoft_enabled %} {% if oauth_microsoft_enabled %}
<a href="{{ url_for('auth.oauth_provider_login', provider='microsoft') }}" <a href="{{ url_for('auth.oauth_provider_login', provider='microsoft') }}"
class="w-full inline-flex items-center justify-center gap-3 px-4 py-3 bg-white border border-brand-border rounded-[0.75rem] text-brand-navy font-semibold hover:bg-brand-bg transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> class="w-full inline-flex items-center justify-center gap-3 px-4 py-3 bg-white border border-brand-border rounded-lg text-brand-navy font-semibold hover:bg-brand-bg transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
{# Official Microsoft 4-square logo #} {# Official Microsoft 4-square logo #}
<svg width="20" height="20" viewBox="0 0 21 21" aria-hidden="true" focusable="false"> <svg width="20" height="20" viewBox="0 0 21 21" aria-hidden="true" focusable="false">
<rect x="1" y="1" width="9" height="9" fill="#F25022"/> <rect x="1" y="1" width="9" height="9" fill="#F25022"/>
@@ -42,7 +42,7 @@
{% if oauth_google_enabled %} {% if oauth_google_enabled %}
<a href="{{ url_for('auth.oauth_provider_login', provider='google') }}" <a href="{{ url_for('auth.oauth_provider_login', provider='google') }}"
class="w-full inline-flex items-center justify-center gap-3 px-4 py-3 bg-white border border-brand-border rounded-[0.75rem] text-brand-navy font-semibold hover:bg-brand-bg transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> class="w-full inline-flex items-center justify-center gap-3 px-4 py-3 bg-white border border-brand-border rounded-lg text-brand-navy font-semibold hover:bg-brand-bg transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
{# Official Google "G" logo #} {# Official Google "G" logo #}
<svg width="20" height="20" viewBox="0 0 24 24" aria-hidden="true" focusable="false"> <svg width="20" height="20" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.76h3.56c2.08-1.92 3.28-4.74 3.28-8.09Z"/> <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.76h3.56c2.08-1.92 3.28-4.74 3.28-8.09Z"/>
@@ -56,7 +56,7 @@
{% if sso_enabled %} {% if sso_enabled %}
<a href="{{ url_for('auth.sso_login') }}" <a href="{{ url_for('auth.sso_login') }}"
class="w-full inline-flex items-center justify-center gap-3 px-4 py-3 bg-white border border-brand-border rounded-[0.75rem] text-brand-navy font-semibold hover:bg-brand-bg transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> class="w-full inline-flex items-center justify-center gap-3 px-4 py-3 bg-white border border-brand-border rounded-lg text-brand-navy font-semibold hover:bg-brand-bg transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
<rect x="3" y="11" width="18" height="11" rx="2"/> <rect x="3" y="11" width="18" height="11" rx="2"/>
<path d="M7 11V7a5 5 0 0 1 10 0v4"/> <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
@@ -81,13 +81,13 @@
<div> <div>
<label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label> <label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label>
{{ form.email(id='email', type='email', autocomplete='email', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.email(id='email', type='email', autocomplete='email', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.email.errors %}<p class="text-xs text-red-700 mt-1" role="alert">{{ form.email.errors[0] }}</p>{% endif %} {% if form.email.errors %}<p class="text-xs text-red-700 mt-1" role="alert">{{ form.email.errors[0] }}</p>{% endif %}
</div> </div>
<div> <div>
<label for="password" class="block text-sm font-medium text-brand-navy mb-1">Mot de passe <span class="text-red-600" aria-hidden="true">*</span></label> <label for="password" class="block text-sm font-medium text-brand-navy mb-1">Mot de passe <span class="text-red-600" aria-hidden="true">*</span></label>
{{ form.password(id='password', autocomplete='current-password', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.password(id='password', autocomplete='current-password', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.password.errors %}<p class="text-xs text-red-700 mt-1" role="alert">{{ form.password.errors[0] }}</p>{% endif %} {% if form.password.errors %}<p class="text-xs text-red-700 mt-1" role="alert">{{ form.password.errors[0] }}</p>{% endif %}
</div> </div>
@@ -99,7 +99,7 @@
<a href="{{ url_for('auth.forgot_password') }}" class="grad-text font-semibold hover:underline">Mot de passe oublié&nbsp;?</a> <a href="{{ url_for('auth.forgot_password') }}" class="grad-text font-semibold hover:underline">Mot de passe oublié&nbsp;?</a>
</div> </div>
<button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> <button type="submit" class="w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Se connecter Se connecter
</button> </button>
</form> </form>

View File

@@ -3,10 +3,10 @@
so dynamic `col-span-{{ span }}` would produce dead classes — the lookup keeps the utilities discoverable). #} so dynamic `col-span-{{ span }}` would produce dead classes — the lookup keeps the utilities discoverable). #}
{% macro bento_card(number, title, description, icon='✦', span='1') %} {% macro bento_card(number, title, description, icon='✦', span='1') %}
{%- set span_classes = {'1': 'col-span-1', '2': 'sm:col-span-2', '3': 'sm:col-span-2 md:col-span-3'} -%} {%- set span_classes = {'1': 'col-span-1', '2': 'sm:col-span-2', '3': 'sm:col-span-2 md:col-span-3'} -%}
<div class="relative bg-brand-navy2 p-6 rounded-[18px] overflow-hidden border border-white/[0.045] {{ span_classes.get(span, 'col-span-1') }}"> <div class="relative bg-brand-navy2 p-6 rounded-xl overflow-hidden border border-white/[0.045] {{ span_classes.get(span, 'col-span-1') }}">
<div class="absolute top-2 right-4 text-[80px] font-black text-white/[0.04]" aria-hidden="true">{{ number }}</div> <div class="absolute top-2 right-4 text-[80px] font-black text-white/[0.04]" aria-hidden="true">{{ number }}</div>
<div class="relative"> <div class="relative">
<div class="w-10 h-10 grad-bg rounded-[0.5rem] mb-4 flex items-center justify-center text-lg">{{ icon }}</div> <div class="w-10 h-10 grad-bg rounded-md mb-4 flex items-center justify-center text-lg">{{ icon }}</div>
<h3 class="text-lg font-bold mb-2 text-white">{{ title | safe }}</h3> <h3 class="text-lg font-bold mb-2 text-white">{{ title | safe }}</h3>
<p class="text-sm text-white/70">{{ description | safe }}</p> <p class="text-sm text-white/70">{{ description | safe }}</p>
</div> </div>

View File

@@ -14,13 +14,13 @@
{%- set sizing = sizes.get(size, sizes['md']) -%} {%- set sizing = sizes.get(size, sizes['md']) -%}
{%- if as_button -%} {%- if as_button -%}
<button type="{{ type }}" <button type="{{ type }}"
class="inline-flex items-center justify-center gap-2 rounded-[0.75rem] font-semibold transition-all duration-200 {{ classes }} {{ sizing }}"> class="inline-flex items-center justify-center gap-2 rounded-lg font-semibold transition-all duration-200 {{ classes }} {{ sizing }}">
<span>{{ text }}</span> <span>{{ text }}</span>
{%- if icon -%}<span class="ml-0.5" aria-hidden="true">{{ icon | safe }}</span>{%- endif -%} {%- if icon -%}<span class="ml-0.5" aria-hidden="true">{{ icon | safe }}</span>{%- endif -%}
</button> </button>
{%- else -%} {%- else -%}
<a href="{{ href }}" <a href="{{ href }}"
class="inline-flex items-center justify-center gap-2 rounded-[0.75rem] font-semibold transition-all duration-200 {{ classes }} {{ sizing }}" class="inline-flex items-center justify-center gap-2 rounded-lg font-semibold transition-all duration-200 {{ classes }} {{ sizing }}"
{% if target %}target="{{ target }}"{% endif %} {% if target %}target="{{ target }}"{% endif %}
{% if rel %}rel="{{ rel }}"{% endif %}> {% if rel %}rel="{{ rel }}"{% endif %}>
<span>{{ text }}</span> <span>{{ text }}</span>

View File

@@ -15,9 +15,9 @@
The button macro autoescapes its `text` arg, so `name` MUST NOT contain HTML entities The button macro autoescapes its `text` arg, so `name` MUST NOT contain HTML entities
(verified: "DictIA 8", "DictIA 16", "DictIA Cloud" are all entity-free). #} (verified: "DictIA 8", "DictIA 16", "DictIA Cloud" are all entity-free). #}
{%- macro pricing_card(slug, name, price_setup, price_monthly, target, features, recommended=False, cta_url='/checkout') -%} {%- macro pricing_card(slug, name, price_setup, price_monthly, target, features, recommended=False, cta_url='/checkout') -%}
<div class="relative {% if recommended %}grad-bg p-[1.5px] rounded-[20px]{% endif %}"> <div class="relative {% if recommended %}grad-bg p-[1.5px] rounded-xl{% endif %}">
{% if recommended %}<span class="absolute -top-3 left-1/2 -translate-x-1/2 grad-bg text-white text-xs font-bold px-3 py-1 rounded-full shadow-cta">★ RECOMMANDÉ</span>{% endif %} {% if recommended %}<span class="absolute -top-3 left-1/2 -translate-x-1/2 grad-bg text-white text-xs font-bold px-3 py-1 rounded-full shadow-cta">★ RECOMMANDÉ</span>{% endif %}
<div class="bg-white p-8 rounded-[18px] border border-brand-border h-full flex flex-col"> <div class="bg-white p-8 rounded-xl border border-brand-border h-full flex flex-col">
<div class="mb-6"> <div class="mb-6">
<h3 class="text-xl font-black mb-1 text-brand-navy">{{ name | safe }}</h3> <h3 class="text-xl font-black mb-1 text-brand-navy">{{ name | safe }}</h3>
<p class="text-sm text-brand-navy/70">{{ target | safe }}</p> <p class="text-sm text-brand-navy/70">{{ target | safe }}</p>

View File

@@ -52,8 +52,8 @@
'desc': 'Fork du projet open source Speakr — architecture entièrement auditable sur <a href="https://gitea.dictia.ca/Innova-AI/dictia-public" target="_blank" rel="noopener" class="underline hover:text-brand-navy">Gitea public</a>. Aucune boîte noire. Vos auditeurs peuvent examiner chaque ligne.' 'desc': 'Fork du projet open source Speakr — architecture entièrement auditable sur <a href="https://gitea.dictia.ca/Innova-AI/dictia-public" target="_blank" rel="noopener" class="underline hover:text-brand-navy">Gitea public</a>. Aucune boîte noire. Vos auditeurs peuvent examiner chaque ligne.'
} }
] %} ] %}
<article class="bg-brand-bg p-6 rounded-[14px] border border-brand-border"> <article class="bg-brand-bg p-6 rounded-xl border border-brand-border">
<div class="w-10 h-10 grad-bg rounded-[0.5rem] mb-4 flex items-center justify-center text-lg shadow-cta" aria-hidden="true">{{ card.icon }}</div> <div class="w-10 h-10 grad-bg rounded-md mb-4 flex items-center justify-center text-lg shadow-cta" aria-hidden="true">{{ card.icon }}</div>
<h3 class="text-lg font-bold mb-2 text-brand-navy">{{ card.title | safe }}</h3> <h3 class="text-lg font-bold mb-2 text-brand-navy">{{ card.title | safe }}</h3>
<p class="text-sm text-brand-navy/80 leading-relaxed">{{ card.desc | safe }}</p> <p class="text-sm text-brand-navy/80 leading-relaxed">{{ card.desc | safe }}</p>
</article> </article>
@@ -93,7 +93,7 @@
'desc': 'Avant tout enregistrement, DictIA exige une confirmation que les participants ont consenti à l\'enregistrement et à la transcription IA. Le consentement est tracé dans le journal d\'audit. Vous pouvez configurer une demande de consentement automatique en début de session.' 'desc': 'Avant tout enregistrement, DictIA exige une confirmation que les participants ont consenti à l\'enregistrement et à la transcription IA. Le consentement est tracé dans le journal d\'audit. Vous pouvez configurer une demande de consentement automatique en début de session.'
} }
] %} ] %}
<article class="bg-white p-6 rounded-[14px] border border-brand-border"> <article class="bg-white p-6 rounded-xl border border-brand-border">
<div class="flex flex-col md:flex-row md:items-start gap-4"> <div class="flex flex-col md:flex-row md:items-start gap-4">
<div class="flex-shrink-0"> <div class="flex-shrink-0">
<span class="inline-block bg-brand-navy text-white text-xs font-black px-3 py-1.5 rounded-md">{{ art.num | safe }}</span> <span class="inline-block bg-brand-navy text-white text-xs font-black px-3 py-1.5 rounded-md">{{ art.num | safe }}</span>

View File

@@ -25,8 +25,8 @@
<div class="grid md:grid-cols-3 gap-6"> <div class="grid md:grid-cols-3 gap-6">
{# Email card #} {# Email card #}
<article class="bg-white p-8 rounded-[18px] border border-brand-border flex flex-col"> <article class="bg-white p-8 rounded-xl border border-brand-border flex flex-col">
<div class="w-12 h-12 grad-bg rounded-[0.5rem] mb-4 flex items-center justify-center text-2xl shadow-cta" aria-hidden="true">✉️</div> <div class="w-12 h-12 grad-bg rounded-md mb-4 flex items-center justify-center text-2xl shadow-cta" aria-hidden="true">✉️</div>
<h3 class="text-lg font-bold mb-2 text-brand-navy">Courriel</h3> <h3 class="text-lg font-bold mb-2 text-brand-navy">Courriel</h3>
<p class="text-sm text-brand-navy/80 mb-4 leading-relaxed flex-grow"> <p class="text-sm text-brand-navy/80 mb-4 leading-relaxed flex-grow">
Privilégiez le courriel pour&nbsp;: pré-inscription, devis, démonstration, dossier de conformité, partenariats. Privilégiez le courriel pour&nbsp;: pré-inscription, devis, démonstration, dossier de conformité, partenariats.
@@ -35,8 +35,8 @@
</article> </article>
{# Phone card #} {# Phone card #}
<article class="bg-white p-8 rounded-[18px] border border-brand-border flex flex-col"> <article class="bg-white p-8 rounded-xl border border-brand-border flex flex-col">
<div class="w-12 h-12 grad-bg rounded-[0.5rem] mb-4 flex items-center justify-center text-2xl shadow-cta" aria-hidden="true">☎️</div> <div class="w-12 h-12 grad-bg rounded-md mb-4 flex items-center justify-center text-2xl shadow-cta" aria-hidden="true">☎️</div>
<h3 class="text-lg font-bold mb-2 text-brand-navy">Téléphone</h3> <h3 class="text-lg font-bold mb-2 text-brand-navy">Téléphone</h3>
<p class="text-sm text-brand-navy/80 mb-4 leading-relaxed flex-grow"> <p class="text-sm text-brand-navy/80 mb-4 leading-relaxed flex-grow">
Du lundi au vendredi, 9&nbsp;h à 17&nbsp;h (heure de l'Est). Laissez un message en dehors de ces heures. Du lundi au vendredi, 9&nbsp;h à 17&nbsp;h (heure de l'Est). Laissez un message en dehors de ces heures.
@@ -45,8 +45,8 @@
</article> </article>
{# Mailing address card #} {# Mailing address card #}
<article class="bg-white p-8 rounded-[18px] border border-brand-border flex flex-col"> <article class="bg-white p-8 rounded-xl border border-brand-border flex flex-col">
<div class="w-12 h-12 grad-bg rounded-[0.5rem] mb-4 flex items-center justify-center text-2xl shadow-cta" aria-hidden="true">📬</div> <div class="w-12 h-12 grad-bg rounded-md mb-4 flex items-center justify-center text-2xl shadow-cta" aria-hidden="true">📬</div>
<h3 class="text-lg font-bold mb-2 text-brand-navy">Bureau</h3> <h3 class="text-lg font-bold mb-2 text-brand-navy">Bureau</h3>
<p class="text-sm text-brand-navy/80 mb-4 leading-relaxed flex-grow"> <p class="text-sm text-brand-navy/80 mb-4 leading-relaxed flex-grow">
Sur rendez-vous uniquement. Visites en personne pour démonstrations on-premise et déploiements DictIA 16 corporatifs. Sur rendez-vous uniquement. Visites en personne pour démonstrations on-premise et déploiements DictIA 16 corporatifs.
@@ -83,7 +83,7 @@
{'label': 'Question média / presse', 'subject': 'Question%20m%C3%A9dia', 'icon': '📰'} {'label': 'Question média / presse', 'subject': 'Question%20m%C3%A9dia', 'icon': '📰'}
] %} ] %}
<a href="mailto:info@dictia.ca?subject={{ shortcut.subject }}" <a href="mailto:info@dictia.ca?subject={{ shortcut.subject }}"
class="flex items-center gap-3 bg-brand-bg p-4 rounded-[14px] border border-brand-border hover:bg-white hover:border-brand-b1/30 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"> class="flex items-center gap-3 bg-brand-bg p-4 rounded-xl border border-brand-border hover:bg-white hover:border-brand-b1/30 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
<span class="text-2xl flex-shrink-0" aria-hidden="true">{{ shortcut.icon }}</span> <span class="text-2xl flex-shrink-0" aria-hidden="true">{{ shortcut.icon }}</span>
<span class="text-sm font-semibold text-brand-navy">{{ shortcut.label | safe }}</span> <span class="text-sm font-semibold text-brand-navy">{{ shortcut.label | safe }}</span>
</a> </a>

View File

@@ -25,7 +25,7 @@
{# NOTE: bento card content is duplicated between landing.html and fonctionnalites.html. {# NOTE: bento card content is duplicated between landing.html and fonctionnalites.html.
When editing, sync both files. Future refactor: extract to _partials/_bento_features.html. #} When editing, sync both files. Future refactor: extract to _partials/_bento_features.html. #}
{% from 'macros/bento.html' import bento_card %} {% from 'macros/bento.html' import bento_card %}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-[1.5px] bg-brand-border rounded-[18px] overflow-hidden"> <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-[1.5px] bg-brand-border rounded-xl overflow-hidden">
{{ bento_card('01', 'Transcription WhisperX', 'Large-v3 fine-tuné FR-CA. Précision 95&nbsp;%+ sur réunions, dictées, audiences (méthodologie disponible sur demande).', '🎙️') }} {{ bento_card('01', 'Transcription WhisperX', 'Large-v3 fine-tuné FR-CA. Précision 95&nbsp;%+ sur réunions, dictées, audiences (méthodologie disponible sur demande).', '🎙️') }}
{{ bento_card('02', 'Diarisation 8 locuteurs', 'pyannote sépare automatiquement les intervenants. Identification par embeddings vocaux.', '👥') }} {{ bento_card('02', 'Diarisation 8 locuteurs', 'pyannote sépare automatiquement les intervenants. Identification par embeddings vocaux.', '👥') }}
{{ bento_card('03', 'Résumés Mistral 7B', 'IA locale génère résumés, points d\'action et procès-verbaux. Aucune connexion cloud.', '📝') }} {{ bento_card('03', 'Résumés Mistral 7B', 'IA locale génère résumés, points d\'action et procès-verbaux. Aucune connexion cloud.', '📝') }}
@@ -55,7 +55,7 @@
{'ext': 'JSON', 'use': 'Pipeline développeur'}, {'ext': 'JSON', 'use': 'Pipeline développeur'},
{'ext': 'MD', 'use': 'Notion, Obsidian, GitHub'} {'ext': 'MD', 'use': 'Notion, Obsidian, GitHub'}
] %} ] %}
<div class="bg-white p-4 rounded-[12px] border border-brand-border text-center"> <div class="bg-white p-4 rounded-xl border border-brand-border text-center">
<p class="text-base font-black text-brand-navy">{{ fmt.ext }}</p> <p class="text-base font-black text-brand-navy">{{ fmt.ext }}</p>
<p class="text-xs text-brand-navy/70 mt-1">{{ fmt.use | safe }}</p> <p class="text-xs text-brand-navy/70 mt-1">{{ fmt.use | safe }}</p>
</div> </div>
@@ -87,7 +87,7 @@
{'name': 'Make (Integromat)', 'desc': 'Scénarios visuels'}, {'name': 'Make (Integromat)', 'desc': 'Scénarios visuels'},
{'name': 'n8n', 'desc': 'Open source self-host'} {'name': 'n8n', 'desc': 'Open source self-host'}
] %} ] %}
<div class="bg-brand-bg p-5 rounded-[14px] border border-brand-border text-center"> <div class="bg-brand-bg p-5 rounded-xl border border-brand-border text-center">
<p class="text-base font-bold text-brand-navy">{{ integ.name | safe }}</p> <p class="text-base font-bold text-brand-navy">{{ integ.name | safe }}</p>
<p class="text-xs text-brand-navy/70 mt-1">{{ integ.desc | safe }}</p> <p class="text-xs text-brand-navy/70 mt-1">{{ integ.desc | safe }}</p>
</div> </div>
@@ -117,7 +117,7 @@
{'title': 'Audio supportés', 'desc': 'WAV, MP3, M4A, FLAC, OGG, WebM — jusqu\'à 8&nbsp;h par fichier. Conversion ffmpeg automatique.'}, {'title': 'Audio supportés', 'desc': 'WAV, MP3, M4A, FLAC, OGG, WebM — jusqu\'à 8&nbsp;h par fichier. Conversion ffmpeg automatique.'},
{'title': 'Langues', 'desc': 'Optimisé français québécois. Aussi&nbsp;: français de France, anglais (canadien et US), espagnol, allemand, mandarin, russe.'} {'title': 'Langues', 'desc': 'Optimisé français québécois. Aussi&nbsp;: français de France, anglais (canadien et US), espagnol, allemand, mandarin, russe.'}
] %} ] %}
<article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded-[0.75rem] border border-white/[0.08]"> <article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded-lg border border-white/[0.08]">
<h3 class="text-lg font-bold mb-2 text-white">{{ spec.title | safe }}</h3> <h3 class="text-lg font-bold mb-2 text-white">{{ spec.title | safe }}</h3>
<p class="text-sm text-white/80 leading-relaxed">{{ spec.desc | safe }}</p> <p class="text-sm text-white/80 leading-relaxed">{{ spec.desc | safe }}</p>
</article> </article>

View File

@@ -140,7 +140,7 @@
('Loi 25 — biométrie', 'Sanctions CAI jusqu\'à 25&nbsp;M$', 'La voix est une donnée biométrique au sens des articles 44-45 de la LCCJTI et un renseignement sensible au sens de la Loi 25 (art.&nbsp;12 LSP). Tout traitement nécessite un consentement explicite, une déclaration préalable à la CAI et un transfert vers un territoire offrant une protection équivalente — ce que les États-Unis n\'offrent pas.', '🛡️'), ('Loi 25 — biométrie', 'Sanctions CAI jusqu\'à 25&nbsp;M$', 'La voix est une donnée biométrique au sens des articles 44-45 de la LCCJTI et un renseignement sensible au sens de la Loi 25 (art.&nbsp;12 LSP). Tout traitement nécessite un consentement explicite, une déclaration préalable à la CAI et un transfert vers un territoire offrant une protection équivalente — ce que les États-Unis n\'offrent pas.', '🛡️'),
('Sanctions disciplinaires', '~250&nbsp;000 pros réglementés QC (CIQ)', 'Les ordres professionnels québécois — au premier rang desquels le Barreau, la Chambre des notaires et CPA Québec — exigent une obligation stricte de confidentialité. Le transfert de données client hors-juridiction sans consentement explicite peut être qualifié de manquement, jusqu\'à la radiation pour les fautes graves.', '⚠️') ('Sanctions disciplinaires', '~250&nbsp;000 pros réglementés QC (CIQ)', 'Les ordres professionnels québécois — au premier rang desquels le Barreau, la Chambre des notaires et CPA Québec — exigent une obligation stricte de confidentialité. Le transfert de données client hors-juridiction sans consentement explicite peut être qualifié de manquement, jusqu\'à la radiation pour les fautes graves.', '⚠️')
] %} ] %}
<article class="bg-white p-6 rounded-[0.75rem] border border-brand-border"> <article class="bg-white p-6 rounded-lg border border-brand-border">
<div class="text-3xl mb-4" aria-hidden="true">{{ card[3] }}</div> <div class="text-3xl mb-4" aria-hidden="true">{{ card[3] }}</div>
<h3 class="text-lg font-bold mb-1 text-brand-navy">{{ card[0] }}</h3> <h3 class="text-lg font-bold mb-1 text-brand-navy">{{ card[0] }}</h3>
<p class="text-xs uppercase tracking-wider text-brand-navy/70 mb-3 font-semibold">{{ card[1] | safe }}</p> <p class="text-xs uppercase tracking-wider text-brand-navy/70 mb-3 font-semibold">{{ card[1] | safe }}</p>
@@ -175,8 +175,8 @@
('Conforme Loi&nbsp;25', 'Audit trail intégré (qui a écouté quoi, quand). Déclaration CAI prête. Consentement explicite tracé pour chaque enregistrement. Code source AGPL v3 — transparence vérifiable.'), ('Conforme Loi&nbsp;25', 'Audit trail intégré (qui a écouté quoi, quand). Déclaration CAI prête. Consentement explicite tracé pour chaque enregistrement. Code source AGPL v3 — transparence vérifiable.'),
('Précision FR-CA', 'WhisperX Large-v3 fine-tuné français québécois. Diarisation pyannote 8 locuteurs. Résumés Mistral 7B local — aucune connexion OpenAI/Google/Microsoft.') ('Précision FR-CA', 'WhisperX Large-v3 fine-tuné français québécois. Diarisation pyannote 8 locuteurs. Résumés Mistral 7B local — aucune connexion OpenAI/Google/Microsoft.')
] %} ] %}
<article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded-[0.75rem] border border-white/[0.08]"> <article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded-lg border border-white/[0.08]">
<div class="w-10 h-10 grad-bg rounded-[0.5rem] mb-4 flex items-center justify-center font-black text-white shadow-cta" aria-hidden="true"></div> <div class="w-10 h-10 grad-bg rounded-md mb-4 flex items-center justify-center font-black text-white shadow-cta" aria-hidden="true"></div>
<h3 class="text-lg font-bold mb-2 text-white">{{ pillar[0] | safe }}</h3> <h3 class="text-lg font-bold mb-2 text-white">{{ pillar[0] | safe }}</h3>
<p class="text-sm text-white/70 leading-relaxed">{{ pillar[1] | safe }}</p> <p class="text-sm text-white/70 leading-relaxed">{{ pillar[1] | safe }}</p>
</article> </article>
@@ -198,7 +198,7 @@
{# NOTE: bento card content is duplicated between landing.html and fonctionnalites.html. {# NOTE: bento card content is duplicated between landing.html and fonctionnalites.html.
When editing, sync both files. Future refactor: extract to _partials/_bento_features.html. #} When editing, sync both files. Future refactor: extract to _partials/_bento_features.html. #}
{% from 'macros/bento.html' import bento_card %} {% from 'macros/bento.html' import bento_card %}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-[1.5px] bg-brand-border rounded-[18px] overflow-hidden"> <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-[1.5px] bg-brand-border rounded-xl overflow-hidden">
{{ bento_card('01', 'Transcription WhisperX', 'Large-v3 fine-tuné FR-CA. Précision 95&nbsp;%+ sur réunions, dictées, audiences (méthodologie disponible sur demande).', '🎙️') }} {{ bento_card('01', 'Transcription WhisperX', 'Large-v3 fine-tuné FR-CA. Précision 95&nbsp;%+ sur réunions, dictées, audiences (méthodologie disponible sur demande).', '🎙️') }}
{{ bento_card('02', 'Diarisation 8 locuteurs', 'pyannote sépare automatiquement les intervenants. Identification par embeddings vocaux.', '👥') }} {{ bento_card('02', 'Diarisation 8 locuteurs', 'pyannote sépare automatiquement les intervenants. Identification par embeddings vocaux.', '👥') }}
{{ bento_card('03', 'Résumés Mistral 7B', 'IA locale génère résumés, points d\'action et procès-verbaux. Aucune connexion cloud.', '📝') }} {{ bento_card('03', 'Résumés Mistral 7B', 'IA locale génère résumés, points d\'action et procès-verbaux. Aucune connexion cloud.', '📝') }}
@@ -225,7 +225,7 @@
{% include 'marketing/_partials/_pricing_tiers.html' %} {% include 'marketing/_partials/_pricing_tiers.html' %}
{# ROI CALCULATOR — Alpine.js, hypotheses transparentes pour LPC art. 219 hygiene #} {# ROI CALCULATOR — Alpine.js, hypotheses transparentes pour LPC art. 219 hygiene #}
<div x-data="roiCalculator()" class="mt-16 max-w-3xl mx-auto bg-white p-8 rounded-[18px] border border-brand-border" aria-labelledby="roi-title"> <div x-data="roiCalculator()" class="mt-16 max-w-3xl mx-auto bg-white p-8 rounded-xl border border-brand-border" aria-labelledby="roi-title">
<p class="eyebrow text-center grad-text mb-2">CALCULATEUR ROI</p> <p class="eyebrow text-center grad-text mb-2">CALCULATEUR ROI</p>
<h3 id="roi-title" class="text-2xl font-black text-center mb-6 text-brand-navy">Combien DictIA peut vous faire économiser&nbsp;?</h3> <h3 id="roi-title" class="text-2xl font-black text-center mb-6 text-brand-navy">Combien DictIA peut vous faire économiser&nbsp;?</h3>
<div class="grid sm:grid-cols-3 gap-4 mb-6"> <div class="grid sm:grid-cols-3 gap-4 mb-6">
@@ -272,7 +272,7 @@
</p> </p>
</div> </div>
<div class="overflow-x-auto rounded-[18px] border border-brand-border"> <div class="overflow-x-auto rounded-xl border border-brand-border">
<table class="w-full min-w-[720px] text-sm"> <table class="w-full min-w-[720px] text-sm">
<caption class="sr-only">Comparaison DictIA, Microsoft Teams Premium, Otter.ai Business, Whisper local sur 6 critères</caption> <caption class="sr-only">Comparaison DictIA, Microsoft Teams Premium, Otter.ai Business, Whisper local sur 6 critères</caption>
<thead class="bg-brand-bg"> <thead class="bg-brand-bg">
@@ -390,8 +390,8 @@
'desc': 'Architecture entièrement auditable sur <a href="https://gitea.innova-ai.ca/Innova-AI/dictia-public" target="_blank" rel="noopener" class="underline hover:text-white">Gitea public</a>. Aucune boîte noire. Vos auditeurs peuvent examiner chaque ligne.' 'desc': 'Architecture entièrement auditable sur <a href="https://gitea.innova-ai.ca/Innova-AI/dictia-public" target="_blank" rel="noopener" class="underline hover:text-white">Gitea public</a>. Aucune boîte noire. Vos auditeurs peuvent examiner chaque ligne.'
} }
] %} ] %}
<article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded-[0.75rem] border border-white/[0.08]"> <article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded-lg border border-white/[0.08]">
<div class="w-10 h-10 grad-bg rounded-[0.5rem] mb-4 flex items-center justify-center text-lg shadow-cta" aria-hidden="true">{{ card.icon }}</div> <div class="w-10 h-10 grad-bg rounded-md mb-4 flex items-center justify-center text-lg shadow-cta" aria-hidden="true">{{ card.icon }}</div>
<h3 class="text-lg font-bold mb-2 text-white">{{ card.title | safe }}</h3> <h3 class="text-lg font-bold mb-2 text-white">{{ card.title | safe }}</h3>
<p class="text-sm text-white/80 leading-relaxed">{{ card.desc | safe }}</p> <p class="text-sm text-white/80 leading-relaxed">{{ card.desc | safe }}</p>
</article> </article>
@@ -421,7 +421,7 @@
<div class="grid md:grid-cols-3 gap-6"> <div class="grid md:grid-cols-3 gap-6">
{% for t in testimonials %} {% for t in testimonials %}
<article class="bg-white p-6 rounded-[18px] border border-brand-border flex flex-col items-center text-center" <article class="bg-white p-6 rounded-xl border border-brand-border flex flex-col items-center text-center"
aria-label="Témoignage {{ t.placeholder_label }} à venir"> aria-label="Témoignage {{ t.placeholder_label }} à venir">
<div class="w-16 h-16 rounded-full grad-bg flex items-center justify-center mb-4 shadow-cta" aria-hidden="true"> <div class="w-16 h-16 rounded-full grad-bg flex items-center justify-center mb-4 shadow-cta" aria-hidden="true">
{%- if t.persona == 'avocat' -%}<span class="text-2xl">⚖️</span> {%- if t.persona == 'avocat' -%}<span class="text-2xl">⚖️</span>

View File

@@ -36,7 +36,7 @@
</h2> </h2>
</div> </div>
<div class="overflow-x-auto rounded-[18px] border border-brand-border"> <div class="overflow-x-auto rounded-xl border border-brand-border">
<table class="w-full min-w-[720px] text-sm"> <table class="w-full min-w-[720px] text-sm">
<caption class="sr-only">Comparaison détaillée des 3 forfaits DictIA sur 8 caractéristiques</caption> <caption class="sr-only">Comparaison détaillée des 3 forfaits DictIA sur 8 caractéristiques</caption>
<thead class="bg-brand-bg"> <thead class="bg-brand-bg">

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="signup-title"> <section class="min-h-[calc(100vh-62px)] bg-brand-bg py-16 px-4" aria-labelledby="signup-title">
<div class="max-w-md mx-auto bg-white p-8 rounded-[18px] border border-brand-border shadow-cta"> <div class="max-w-md mx-auto bg-white p-8 rounded-xl border border-brand-border shadow-cta">
<h1 id="signup-title" class="text-3xl font-black text-brand-navy mb-2">Créer un compte</h1> <h1 id="signup-title" class="text-3xl font-black text-brand-navy mb-2">Créer un compte</h1>
<p class="text-sm text-brand-navy/70 mb-6">{{ "Conformité Loi&nbsp;25 incluse — consentement granulaire, hébergement au Québec." | safe }}</p> <p class="text-sm text-brand-navy/70 mb-6">{{ "Conformité Loi&nbsp;25 incluse — consentement granulaire, hébergement au Québec." | safe }}</p>
@@ -28,45 +28,45 @@
<div> <div>
<label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label> <label for="email" class="block text-sm font-medium text-brand-navy mb-1">Courriel <span class="text-red-600" aria-hidden="true">*</span></label>
{{ form.email(id='email', type='email', autocomplete='email', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.email(id='email', type='email', autocomplete='email', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.email.errors %}<p class="text-xs text-red-700 mt-1">{{ form.email.errors[0] }}</p>{% endif %} {% if form.email.errors %}<p class="text-xs text-red-700 mt-1">{{ form.email.errors[0] }}</p>{% endif %}
</div> </div>
<div> <div>
<label for="password" class="block text-sm font-medium text-brand-navy mb-1">Mot de passe <span class="text-red-600" aria-hidden="true">*</span></label> <label for="password" class="block text-sm font-medium text-brand-navy mb-1">Mot de passe <span class="text-red-600" aria-hidden="true">*</span></label>
{{ form.password(id='password', autocomplete='new-password', required=true, minlength=8, **{'aria-required':'true', 'aria-describedby':'password-help', 'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.password(id='password', autocomplete='new-password', required=true, minlength=8, **{'aria-required':'true', 'aria-describedby':'password-help', 'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.password.errors %}<p class="text-xs text-red-900 mt-1">{{ form.password.errors[0] }}</p>{% endif %} {% if form.password.errors %}<p class="text-xs text-red-900 mt-1">{{ form.password.errors[0] }}</p>{% endif %}
<p id="password-help" class="text-xs text-brand-navy/70 mt-1">8&nbsp;caractères minimum, dont une majuscule, une minuscule, un chiffre et un caractère spécial.</p> <p id="password-help" class="text-xs text-brand-navy/70 mt-1">8&nbsp;caractères minimum, dont une majuscule, une minuscule, un chiffre et un caractère spécial.</p>
</div> </div>
<div> <div>
<label for="confirm_password" class="block text-sm font-medium text-brand-navy mb-1">Confirmer le mot de passe <span class="text-red-600" aria-hidden="true">*</span></label> <label for="confirm_password" class="block text-sm font-medium text-brand-navy mb-1">Confirmer le mot de passe <span class="text-red-600" aria-hidden="true">*</span></label>
{{ form.confirm_password(id='confirm_password', autocomplete='new-password', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.confirm_password(id='confirm_password', autocomplete='new-password', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.confirm_password.errors %}<p class="text-xs text-red-700 mt-1">{{ form.confirm_password.errors[0] }}</p>{% endif %} {% if form.confirm_password.errors %}<p class="text-xs text-red-700 mt-1">{{ form.confirm_password.errors[0] }}</p>{% endif %}
</div> </div>
<div class="grid grid-cols-2 gap-3"> <div class="grid grid-cols-2 gap-3">
<div> <div>
<label for="first_name" class="block text-sm font-medium text-brand-navy mb-1">Prénom <span class="text-red-600" aria-hidden="true">*</span></label> <label for="first_name" class="block text-sm font-medium text-brand-navy mb-1">Prénom <span class="text-red-600" aria-hidden="true">*</span></label>
{{ form.first_name(id='first_name', autocomplete='given-name', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.first_name(id='first_name', autocomplete='given-name', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.first_name.errors %}<p class="text-xs text-red-700 mt-1">{{ form.first_name.errors[0] }}</p>{% endif %} {% if form.first_name.errors %}<p class="text-xs text-red-700 mt-1">{{ form.first_name.errors[0] }}</p>{% endif %}
</div> </div>
<div> <div>
<label for="last_name" class="block text-sm font-medium text-brand-navy mb-1">Nom <span class="text-red-600" aria-hidden="true">*</span></label> <label for="last_name" class="block text-sm font-medium text-brand-navy mb-1">Nom <span class="text-red-600" aria-hidden="true">*</span></label>
{{ form.last_name(id='last_name', autocomplete='family-name', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.last_name(id='last_name', autocomplete='family-name', required=true, **{'aria-required':'true', 'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.last_name.errors %}<p class="text-xs text-red-700 mt-1">{{ form.last_name.errors[0] }}</p>{% endif %} {% if form.last_name.errors %}<p class="text-xs text-red-700 mt-1">{{ form.last_name.errors[0] }}</p>{% endif %}
</div> </div>
</div> </div>
<div> <div>
<label for="cabinet" class="block text-sm font-medium text-brand-navy mb-1">Cabinet / Organisation</label> <label for="cabinet" class="block text-sm font-medium text-brand-navy mb-1">Cabinet / Organisation</label>
{{ form.cabinet(id='cabinet', autocomplete='organization', **{'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.cabinet(id='cabinet', autocomplete='organization', **{'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
{% if form.cabinet.errors %}<p class="text-xs text-red-700 mt-1">{{ form.cabinet.errors[0] }}</p>{% endif %} {% if form.cabinet.errors %}<p class="text-xs text-red-700 mt-1">{{ form.cabinet.errors[0] }}</p>{% endif %}
</div> </div>
<div> <div>
<label for="ordre_pro" class="block text-sm font-medium text-brand-navy mb-1">Ordre professionnel</label> <label for="ordre_pro" class="block text-sm font-medium text-brand-navy mb-1">Ordre professionnel</label>
{{ form.ordre_pro(id='ordre_pro', **{'class':'w-full px-3 py-2 border border-brand-border rounded-[0.5rem] text-brand-navy bg-white focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.ordre_pro(id='ordre_pro', **{'class':'w-full px-3 py-2 border border-brand-border rounded-md text-brand-navy bg-white focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
</div> </div>
{# 4 SEPARATE consent checkboxes — Loi 25 art. 14 (consent must be granular, free, informed) #} {# 4 SEPARATE consent checkboxes — Loi 25 art. 14 (consent must be granular, free, informed) #}
@@ -96,7 +96,7 @@
</label> </label>
</fieldset> </fieldset>
{{ form.submit(**{'class':'w-full grad-bg text-white font-semibold py-3 rounded-[0.75rem] shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }} {{ form.submit(**{'class':'w-full grad-bg text-white font-semibold py-3 rounded-lg shadow-cta hover:shadow-cta-hover transition focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2'}) }}
</form> </form>
<p class="text-center text-sm text-brand-navy/70 mt-6">Déjà un compte ? <a href="{{ url_for('auth.login') }}" class="grad-text font-semibold">Se connecter</a></p> <p class="text-center text-sm text-brand-navy/70 mt-6">Déjà un compte ? <a href="{{ url_for('auth.login') }}" class="grad-text font-semibold">Se connecter</a></p>

View File

@@ -268,7 +268,7 @@ def test_bento_uses_flexihub_styling():
assert 'gap-[1.5px]' in body, "Missing FlexiHub ultrafin separator gap" assert 'gap-[1.5px]' in body, "Missing FlexiHub ultrafin separator gap"
assert 'bg-brand-navy2' in body, "Missing dark card background" assert 'bg-brand-navy2' in body, "Missing dark card background"
assert 'text-white/[0.04]' in body, "Missing FlexiHub watermark opacity" assert 'text-white/[0.04]' in body, "Missing FlexiHub watermark opacity"
assert 'grad-bg rounded-[0.5rem]' in body, "Missing gradient icon corner" assert 'grad-bg rounded-md' in body, "Missing gradient icon corner"
def test_bento_responsive_grid(): def test_bento_responsive_grid():
@@ -333,8 +333,8 @@ def test_pricing_recommended_tier_is_dictia_16():
client = app.test_client() client = app.test_client()
body = client.get('/').data.decode('utf-8') body = client.get('/').data.decode('utf-8')
assert 'RECOMMAND' in body, "Missing RECOMMANDÉ badge" assert 'RECOMMAND' in body, "Missing RECOMMANDÉ badge"
# The recommended tier wraps in grad-bg p-[1.5px] rounded-[20px] FlexiHub style # The recommended tier wraps in grad-bg p-[1.5px] rounded-xl FlexiHub style (12px modern)
assert 'grad-bg p-[1.5px] rounded-[20px]' in body, "Missing FlexiHub gradient frame on recommended tier" assert 'grad-bg p-[1.5px] rounded-xl' in body, "Missing FlexiHub gradient frame on recommended tier"
def test_pricing_cta_uses_reserver_pre_launch_wording(): def test_pricing_cta_uses_reserver_pre_launch_wording():