Merge pull request #92 from IliesAllali/fix/fiche-entete-partage

Fiche, partage, landing et bannière de création
This commit is contained in:
IliesAllali authored and GitHub committed 2026-10-18 23:57:45 +02:00
commit b013f0f40c
11 files changed
+167 -95

No files matched your search

+15 -18
View File
@@ -544,31 +544,28 @@
left: 20px;
z-index: 1200;
max-width: 340px;
padding: 12px 14px;
padding: 18px 20px 20px;
display: grid;
gap: 8px;
border-radius: 14px;
border: 1px solid rgba(42, 38, 34, 0.14);
background: rgba(242, 239, 234, 0.95);
box-shadow: 0 6px 24px rgba(52, 48, 44, 0.18);
color: #2a2622;
font-size: 13px;
line-height: 1.45;
gap: 10px;
border-radius: var(--pz-r-l);
border: 1px solid var(--pz-line);
background: var(--pz-surface);
box-shadow: var(--pz-shadow);
color: var(--pz-ink-2);
font: 400 14px/1.5 var(--pz-sans);
}
.tree-created-prompt strong {
color: #2A2622;
font-size: 14px;
color: var(--pz-ink);
font: 400 21px/1.15 var(--pz-serif);
letter-spacing: -.01em;
}
.tree-created-prompt button {
.tree-created-prompt .pz-btn {
justify-self: start;
border: 1px solid #efc7b8;
border-radius: 8px;
background: #ffffff;
color: #2a2622;
padding: 6px 10px;
cursor: pointer;
min-height: 38px;
padding: 0 18px;
font-size: 14px;
}
.settings-card {
+2 -2
View File
@@ -2898,7 +2898,7 @@ function App() {
<div className="tree-created-prompt" role="status">
<strong>{t('Votre arbre est créé.')}</strong>
{t("Ajoutez un proche pour commencer, comme votre mère, votre père ou un frère. Il suffit d'un prénom.")}
<button type="button" onClick={() => setTreeWelcomeVisible(false)} aria-label={t('Fermer')}>
<button type="button" className="pz-btn pz-btn--secondary" onClick={() => setTreeWelcomeVisible(false)} aria-label={t('Fermer')}>
{t('Fermer')}
</button>
</div>
@@ -2908,7 +2908,7 @@ function App() {
<strong>{t("Votre arbre est créé, mais l'import n'a pas abouti.")}</strong>
{` ${treeImportError} `}
{t("Vous pouvez réessayer depuis les paramètres de l'arbre.")}
<button type="button" onClick={() => setTreeImportError('')} aria-label={t('Fermer')}>
<button type="button" className="pz-btn pz-btn--secondary" onClick={() => setTreeImportError('')} aria-label={t('Fermer')}>
{t('Fermer')}
</button>
</div>
+13 -5
View File
@@ -238,6 +238,8 @@ const ContributionPanel = ({
const [newPassword, setNewPassword] = useState('')
const [inviteLocalError, setInviteLocalError] = useState('')
const [copyFeedback, setCopyFeedback] = useState('')
// Le message d'invitation reste replié : une ligne, le texte ne s'ouvre que sur demande
const [showInviteMessage, setShowInviteMessage] = useState(false)
// Load persisted decisions from localStorage
useEffect(() => {
@@ -366,7 +368,6 @@ const ContributionPanel = ({
aria-labelledby="contribPanelTitle"
>
<div className="pz-modal-head">
<p className="pz-eyebrow">{t('Partager et relire')}</p>
<h1 id="contribPanelTitle" className="pz-title">{familyTitleBefore}<em>{t('participe')}</em>{familyTitleAfter}</h1>
</div>
<button type="button" className="pz-btn pz-btn--ghost pz-btn--icon pz-modal-close" onClick={onClose} aria-label={t('Fermer')}>
@@ -412,11 +413,18 @@ const ContributionPanel = ({
</div>
{defaultInviteText && (
<div className="contrib-invite-message">
<label className="contrib-invite-title" htmlFor="contrib-invite-text">{t('Le message à envoyer')}</label>
<textarea id="contrib-invite-text" className="contrib-invite-textarea" rows={9} value={inviteText} onChange={(e) => setInviteText(e.target.value)} />
<div className="contrib-invite-message-actions">
<button type="button" className="pz-btn pz-btn--primary pz-btn--sm" onClick={() => handleCopy(inviteText, t('Message copié'))}><Copy size={14} strokeWidth={2} />{t('Copier le message')}</button>
<div className="contrib-invite-message-row">
<button type="button" className="contrib-pill-btn contrib-pill-btn--edit" onClick={() => handleCopy(inviteText, t('Message copié'))}><Copy size={14} strokeWidth={2} />{t("Copier le message d'invitation")}</button>
<button type="button" className="contrib-link-btn" aria-expanded={showInviteMessage} aria-controls="contrib-invite-text" onClick={() => setShowInviteMessage((v) => !v)}>
{showInviteMessage ? t('Masquer le message') : t('Voir ou modifier le message')}
</button>
</div>
{showInviteMessage && (
<>
<label className="contrib-invite-message-label" htmlFor="contrib-invite-text">{t('Le message à envoyer')}</label>
<textarea id="contrib-invite-text" className="contrib-invite-textarea" rows={6} value={inviteText} onChange={(e) => setInviteText(e.target.value)} />
</>
)}
</div>
)}
{(inviteLocalError || inviteError) && <div className="contrib-error">{inviteLocalError || inviteError}</div>}
+29 -9
View File
@@ -35,8 +35,8 @@
.pc-hidden-input { display: none; }
/* ── En-tête ── */
.pc-head { display: flex; gap: 16px; align-items: flex-start; padding-right: 36px; }
.pc-photo-wrap { display: grid; gap: 8px; justify-items: center; flex: none; }
.pc-head { --pc-name-h: 42px; --pc-name-gap: 6px; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px 16px; align-items: start; padding-right: 36px; }
.pc-photo-wrap { position: relative; display: grid; gap: 8px; justify-items: center; flex: none; }
.pc-photo {
width: 84px; height: 84px; border-radius: 24%; overflow: hidden;
background: var(--pz-a100); box-shadow: 0 0 0 1px rgba(42, 38, 34, .06), var(--pz-shadow);
@@ -54,22 +54,42 @@
.pc-photo--add:active:not(:disabled) { transform: scale(.97); }
.pc-photo--add:focus-visible { outline: 2px solid var(--pz-a500); outline-offset: 3px; }
.pc-photo--add:disabled { opacity: .6; cursor: default; }
.pc-photo-actions { display: grid; gap: 4px; justify-items: center; }
/* Menu du choix de source du portrait : au-dessus de la fiche (z-index 1000), sous la modale Immich (2200) */
.pc-source-backdrop { position: fixed; inset: 0; z-index: 1100; }
.pc-source-menu { position: fixed; z-index: 1150; min-width: 220px; }
/* Mode modification : le portrait prend la hauteur des deux champs Prénom et Nom (écart compris) et reste carré */
.person-card--edit-mode .pc-id { padding-top: 0; }
.person-card--edit-mode .pc-photo-wrap { display: block; }
/* Taille fixée par les champs, pas par le contenu : une vraie photo a une taille propre qui faisait grandir le portrait */
.person-card--edit-mode .pc-photo-wrap > .pc-photo { --pc-photo-edit: calc(var(--pc-name-h) * 2 + var(--pc-name-gap)); width: var(--pc-photo-edit); height: var(--pc-photo-edit); }
.pc-photo-badge {
position: absolute; right: -6px; bottom: -6px; z-index: 1;
width: 30px; height: 30px; display: grid; place-items: center; padding: 0;
border: 0; border-radius: 50%; background: #fff; color: var(--pz-ink-2); cursor: pointer;
box-shadow: 0 0 0 1px rgba(42, 38, 34, .06), var(--pz-shadow);
transition: transform 140ms var(--pz-ease), background-color 160ms ease;
-webkit-tap-highlight-color: transparent;
}
@media (hover: hover) and (pointer: fine) { .pc-photo-badge:hover:not(:disabled) { background: var(--pz-a50); color: var(--pz-a700); } }
.pc-photo-badge:active:not(:disabled) { transform: scale(.94); }
.pc-photo-badge:focus-visible { outline: 2px solid var(--pz-a500); outline-offset: 2px; }
.pc-photo-badge:disabled { opacity: .5; cursor: default; }
.pc-photo-btn {
border: 0; background: none; padding: 2px 4px; cursor: pointer;
font: 500 12px var(--pz-sans); color: var(--pz-a700); text-decoration: underline; text-underline-offset: 2px;
}
.pc-photo-btn--danger { color: var(--pz-danger); }
.pc-photo-btn--danger { color: var(--pz-danger); justify-self: center; }
.pc-photo-btn:disabled { opacity: .5; cursor: default; }
.pc-id { display: grid; gap: 6px; min-width: 0; padding-top: 4px; }
.pc-name { margin: 0; font: 400 28px/1.08 var(--pz-serif); letter-spacing: -.01em; color: var(--pz-ink); text-wrap: balance; }
.pc-name em { font-style: italic; font-weight: 300; }
.pc-nick { margin: 0; font: italic 300 17px/1.3 var(--pz-serif); color: var(--pz-muted); }
.pc-life { margin: 0; font: 400 13.5px/1.4 var(--pz-sans); color: var(--pz-muted); }
.pc-meta { margin: 0; font: 400 13.5px/1.5 var(--pz-sans); color: var(--pz-muted); text-wrap: pretty; }
.pc-meta-item { display: inline; }
.pc-meta-item--nowrap { white-space: nowrap; }
.pc-nick { font: italic 300 15.5px/1 var(--pz-serif); color: var(--pz-ink-2); }
.pc-meta-sep { margin: 0 .45em; color: var(--pz-faint); }
.pc-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pc-chips:empty { display: none; }
/* Place dans la fratrie (mode édition) : ‹ 2 sur 3 › dans une pastille */
@@ -88,7 +108,7 @@
.pc-order-btn:hover:not(:disabled) { background: var(--pz-line-soft); color: var(--pz-ink); }
}
@media (pointer: coarse) { .pc-order-btn { width: 32px; height: 32px; } }
.pc-name-edit { display: grid; gap: 6px; }
.pc-name-edit { display: grid; gap: var(--pc-name-gap); }
/* ── Champs d'édition ── */
.pc-input {
@@ -99,7 +119,7 @@
}
.pc-input:focus { outline: none; border-color: var(--pz-a500); box-shadow: 0 0 0 3px var(--pz-a100); }
.pc-input::placeholder { color: var(--pz-faint); }
.pc-input--name { font: 400 20px/1.2 var(--pz-serif); min-height: 42px; }
.pc-input--name { font: 400 20px/1.2 var(--pz-serif); min-height: var(--pc-name-h); }
.pc-input--last { font-style: italic; }
.pc-note-input { min-height: 88px; resize: vertical; }
@@ -253,7 +273,7 @@
/* Prénom inconnu (issue Anaïs, 18/10) : libellé en italique, case à cocher sous les champs de nom */
.pc-name-unknown { font-style: italic; font-weight: 300; }
.pc-unknown-first { display: inline-flex; align-items: center; gap: 7px; margin-top: 6px; font: 400 13px var(--pz-sans); color: var(--pz-ink-2); cursor: pointer; }
.pc-unknown-first { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; margin: -4px 0 0; font: 400 12.5px var(--pz-sans); color: var(--pz-ink-2); cursor: pointer; white-space: nowrap; }
.pc-unknown-first input { width: 15px; height: 15px; accent-color: var(--pz-a500); }
.pc-name-error { margin: 6px 0 0; font: 500 12.5px var(--pz-sans); color: var(--pz-danger); }
+57 -45
View File
@@ -1,6 +1,6 @@
import { useState, useEffect, useRef, forwardRef } from 'react'
import { createPortal } from 'react-dom'
import { Save, Trash2, Plus, X, Search, Check, Play, ImagePlus, Sparkles, ChevronLeft, ChevronRight } from 'lucide-react'
import { Save, Trash2, Plus, X, Search, Check, Play, ImagePlus, Camera, Sparkles, ChevronLeft, ChevronRight } from 'lucide-react'
import { getParents, getChildren, getPersonUnions, getPersonMedias, getDemoLife, filiations, persons, unions } from '../data/mockData'
import { fetchPersonLife } from '../api/treeApi.js'
import LifeTimeline from './LifeTimeline.jsx'
@@ -508,6 +508,12 @@ const PersonCard = ({
return parts.join(' – ')
})()
const ageLabel = age === null ? null : person.isAlive ? t('{age} ans', { age }) : t('{age} ans au décès', { age })
// Une seule ligne sous le nom : surnom, repères de vie, âge, séparés par un point médian
const metaItems = [
person.nickname && { key: 'nick', node: <em className="pc-nick">{person.nickname}</em>, nowrap: true },
lifeLine && { key: 'life', node: lifeLine },
ageLabel && { key: 'age', node: ageLabel, nowrap: true },
].filter(Boolean)
const renderRelationRow = (target, relationType, meta) => (
<div key={`${relationType}-${target?.id}`} className="pc-rel-row">
@@ -574,9 +580,10 @@ const PersonCard = ({
className="pc-photo pc-photo--add"
onClick={() => avatarInputRef.current?.click()}
disabled={avatarActionLoading}
aria-label={t('Ajouter une photo')}
title={t('Ajouter une photo')}
>
<ImagePlus size={22} strokeWidth={1.8} aria-hidden="true" />
<span>{avatarActionLoading ? t('Envoi') : t('Ajouter une photo')}</span>
</button>
) : (
<div className="pc-photo">
@@ -586,29 +593,29 @@ const PersonCard = ({
</div>
)}
{editMode && onAvatarUpload && (
<div className="pc-photo-actions">
<button
ref={sourceBtnRef}
type="button"
className="pc-photo-btn"
onClick={openAvatarPicker}
disabled={avatarActionLoading}
aria-haspopup="menu"
aria-expanded={showSourceMenu}
>
{person.photo ? t('Changer') : t('Ajouter une photo')}
</button>
{person.photo && (
<button
type="button"
className="pc-photo-btn pc-photo-btn--danger"
onClick={() => onAvatarDelete?.(person.id, person.avatarFromImmich)}
disabled={avatarActionLoading}
>
{t('Retirer')}
</button>
)}
</div>
<button
ref={sourceBtnRef}
type="button"
className="pc-photo-badge"
onClick={openAvatarPicker}
disabled={avatarActionLoading}
aria-label={t('Changer la photo')}
title={t('Changer la photo')}
aria-haspopup="menu"
aria-expanded={showSourceMenu}
>
<Camera size={14} strokeWidth={2} aria-hidden="true" />
</button>
)}
{editMode && onAvatarUpload && person.photo && (
<button
type="button"
className="pc-photo-btn pc-photo-btn--danger"
onClick={() => onAvatarDelete?.(person.id, person.avatarFromImmich)}
disabled={avatarActionLoading}
>
{t('Retirer')}
</button>
)}
</div>
@@ -639,22 +646,6 @@ const PersonCard = ({
placeholder={t('Nom')}
aria-label={t('Nom')}
/>
{/* Visible tant que le prénom est vide, et tant qu'elle est cochée (pour pouvoir la décocher) */}
{(unknownFirstName || !String(getEditValue('firstName', person.firstName) ?? '').trim()) && (
<label className="pc-unknown-first">
<input
type="checkbox"
checked={unknownFirstName}
onChange={(e) => {
setUnknownFirstName(e.target.checked)
setFirstNameMissing(false)
setLastNameMissing(false)
handleFieldChange('firstName', '')
}}
/>
{t('Je ne connais pas son prénom')}
</label>
)}
{firstNameMissing && <p className="pc-name-error">{t('Il faut au moins un prénom.')}</p>}
{lastNameMissing && <p className="pc-name-error">{t('Il faut au moins un nom si le prénom est inconnu.')}</p>}
</div>
@@ -664,10 +655,17 @@ const PersonCard = ({
{person.lastName && <>{' '}<em>{person.lastName}</em></>}
</h2>
)}
{!editMode && person.nickname && <p className="pc-nick">{person.nickname}</p>}
{!editMode && lifeLine && <p className="pc-life">{lifeLine}</p>}
{!editMode && metaItems.length > 0 && (
<p className="pc-meta">
{metaItems.map((item, i) => (
<span key={item.key} className={`pc-meta-item${item.nowrap ? ' pc-meta-item--nowrap' : ''}`}>
{item.node}
{i < metaItems.length - 1 && <span className="pc-meta-sep" aria-hidden="true">·</span>}
</span>
))}
</p>
)}
<div className="pc-chips">
{editMode && <span className="pz-tag pz-tag--accent pz-tag--dot">{t('Édition')}</span>}
{editMode && siblingPlace && (
<span className="pz-tag pc-order" role="group" aria-label={t('Place {n} sur {total} parmi les frères et sœurs', { n: siblingPlace.index + 1, total: siblingPlace.count })}>
<button type="button" className="pc-order-btn" onClick={() => onMoveSibling?.(-1)} disabled={siblingPlace.index === 0} aria-label={t('Déplacer à gauche')} title={t('Déplacer à gauche')}>
@@ -679,10 +677,24 @@ const PersonCard = ({
</button>
</span>
)}
{!editMode && ageLabel && <span className="pz-tag">{ageLabel}</span>}
{!editMode && person.profession && <span className="pz-tag">{person.profession}</span>}
</div>
</div>
{editMode && ((unknownFirstName || !String(getEditValue('firstName', person.firstName) ?? '').trim())) && (
<label className="pc-unknown-first">
<input
type="checkbox"
checked={unknownFirstName}
onChange={(e) => {
setUnknownFirstName(e.target.checked)
setFirstNameMissing(false)
setLastNameMissing(false)
handleFieldChange('firstName', '')
}}
/>
{t('Je ne connais pas son prénom')}
</label>
)}
</header>
{avatarActionError && <div className="pz-error pc-inline-msg">{avatarActionError}</div>}
+4 -1
View File
@@ -67,9 +67,12 @@
.contrib-invite-passwords { display: grid; gap: 6px; }
.contrib-pass-hint { margin: 2px 4px 0; font: 400 12.5px/1.45 var(--pz-sans); color: var(--pz-muted); }
.contrib-invite-message { display: grid; gap: 8px; }
.contrib-invite-message-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 6px 12px; }
.contrib-invite-message-label { font: 500 12.5px var(--pz-sans); color: var(--pz-muted); }
.contrib-link-btn { border: 0; padding: 4px 2px; background: none; cursor: pointer; font: 500 12.5px var(--pz-sans); color: var(--pz-a700); text-decoration: underline; text-underline-offset: 2px; }
.contrib-link-btn:focus-visible { outline: 2px solid var(--pz-a500); outline-offset: 2px; border-radius: 4px; }
.contrib-invite-textarea { width: 100%; box-sizing: border-box; padding: 12px 14px; background: var(--pz-surface); border: 1px solid var(--pz-line); border-radius: var(--pz-r-m); font: 400 13.5px/1.5 var(--pz-sans); color: var(--pz-ink-2); resize: vertical; outline: none; field-sizing: content; min-height: 9.5lh; }
.contrib-invite-textarea:focus { border-color: var(--pz-a500); box-shadow: 0 0 0 3px var(--pz-a100); }
.contrib-invite-message-actions { display: flex; justify-content: flex-end; }
.contrib-role-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 8px 8px 12px; background: var(--pz-surface); border: 1px solid var(--pz-line); border-radius: var(--pz-r-m); }
.contrib-role-badge { display: grid; gap: 1px; min-width: 130px; flex: 1; }
.contrib-role-badge strong { font: 500 14px var(--pz-sans); color: var(--pz-ink); }
+3 -2
View File
@@ -52,7 +52,6 @@ export default {
// ─── Panneau de relecture ───
'Copie indisponible': 'Copy unavailable',
'Minimum 8 caractères.': 'At least 8 characters.',
'Partager et relire': 'Share and review',
'La famille {em}': 'The family {em}',
'participe': 'joins in',
'Fermer': 'Close',
@@ -82,7 +81,9 @@ export default {
'Changing the password means everyone who has already opened the tree will need to enter the new one.',
'Le message à envoyer': 'The message to send',
'Message copié': 'Message copied',
'Copier le message': 'Copy message',
"Copier le message d'invitation": 'Copy invitation message',
'Voir ou modifier le message': 'View or edit the message',
'Masquer le message': 'Hide the message',
'Propositions à relire': 'Suggestions to review',
'Acceptez ou refusez chaque modification, puis appliquez vos choix.': 'Accept or decline each change, then apply your choices.',
'Actualiser': 'Refresh',
+1
View File
@@ -19,6 +19,7 @@ export default {
'Fermer la fiche': 'Close profile',
'Changer': 'Change',
'Ajouter une photo': 'Add a photo',
'Changer la photo': 'Change photo',
'Retirer': 'Remove',
'Prénom': 'First name',
'Nom': 'Last name',
+16 -3
View File
@@ -773,14 +773,27 @@ function Header({ t, onCta, loginHref, elevated }) {
return (
<header className={`site-nav${elevated ? ' is-elevated' : ''}`}>
<div className="wrap nav-in">
<a href={t.home} aria-label={t.logoHome}><LogoLockup height={22} /></a>
<a href={t.home} aria-label={t.logoHome} className="site-logo">
{/* Sur téléphone, le symbole seul : le nom « Parenthèse » reste dans le nom accessible du lien */}
<LogoLockup height={22} className="lg lk lk-full" decorative />
<LogoLockup height={24} mark className="lg lk lk-mark" decorative />
</a>
<nav aria-label="Sections">
<a href="#comment">{t.nav.how}</a>
<a href="#demo">{t.nav.demo}</a>
{t.nav.privacy && <a href={t.privacyHref}>{t.nav.privacy}</a>}
<a href={loginHref}>{t.nav.login}</a>
</nav>
<button type="button" onClick={onCta} className="btn p sm">{t.cta}</button>
<div className="nav-cta">
<a href={loginHref} className="btn s sm nav-login" aria-label={t.nav.login}>
{/* Silhouette de profil, icône « user » de Lucide (ISC), tracée à la main : la landing n'embarque pas la bibliothèque */}
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<circle cx="12" cy="7" r="4" />
<path d="M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1" />
</svg>
<span className="nav-login-text">{t.nav.login}</span>
</a>
<button type="button" onClick={onCta} className="btn p sm">{t.cta}</button>
</div>
</div>
</header>
)
+14 -10
View File
@@ -4,25 +4,29 @@ import { LOGO_NOM } from './logoNomPaths.js'
// Logo de l'en-tête en SVG en ligne, pour animer son arrivée : les parenthèses s'ouvrent,
// le rond se pose, le nom suit. Une fois par visite, décidé dans index.html (classe
// logo-intro sur <html>), animation dans index.css. Même dessin que /logo.svg, rapport 7,541.
export default function LogoLockup({ height = 22 }) {
// Symbole seul (parenthèses et rond, sans le nom) : emprise mesurée sur le dessin, pour le téléphone
export default function LogoLockup({ height = 22, mark = false, decorative = false, className = 'lg lk' }) {
const viewBox = mark ? '-940 -1585 1880 1680' : '-951.9 -1596.5 12857.7 1704.9'
const width = mark ? Math.round(height * 1880 / 1680) : Math.round(height * 7.541)
return (
<svg
className="lg lk"
viewBox="-951.9 -1596.5 12857.7 1704.9"
width={Math.round(height * 7.541)}
className={className}
viewBox={viewBox}
width={width}
height={height}
style={{ height }}
role="img"
aria-label="Parenthèse"
{...(decorative ? { 'aria-hidden': 'true', focusable: 'false' } : { role: 'img', 'aria-label': 'Parenthèse' })}
>
<g className="lk-pl"><path transform="scale(1,-1)" d={PAREN_LEFT} /></g>
<g className="lk-pr"><path transform="scale(1,-1)" d={PAREN_RIGHT} /></g>
<circle className="lk-dot" cx="0" cy="-744" r="424.3" />
<g className="lk-nom">
<g transform="scale(1,-1)">
{LOGO_NOM.map((d, i) => <path key={i} d={d} />)}
{!mark && (
<g className="lk-nom">
<g transform="scale(1,-1)">
{LOGO_NOM.map((d, i) => <path key={i} d={d} />)}
</g>
</g>
</g>
)}
</svg>
)
}
+13
View File
@@ -78,12 +78,18 @@ em.s { font-style: italic; font-weight: 300; }
.site-nav.is-elevated { border-bottom-color: var(--trait); }
.nav-in { display: flex; align-items: center; justify-content: space-between; gap: 16px; height: 68px; }
.nav-in nav { display: flex; gap: 4px; }
.nav-cta { display: flex; align-items: center; gap: 8px; flex: none; }
.nav-login svg { flex: none; }
/* Connexion : fond blanc, sans contour, même hauteur que « Créer mon arbre » (btn sm) */
.btn.s.nav-login { background: #fff; color: var(--encre); box-shadow: none; }
@media (hover: hover) and (pointer: fine) { .btn.s.nav-login:hover { background: var(--a50); } }
.nav-in nav a {
font: 500 14px var(--sans); color: var(--encre2); text-decoration: none;
padding: 8px 12px; border-radius: 999px; transition: background-color 160ms ease;
}
@media (hover: hover) { .nav-in nav a:hover { background: var(--a100); } }
.lg { display: block; width: auto; }
.site-logo .lk-mark { display: none; }
/* logo de l'en-tête (SVG en ligne) */
.lk-pl path, .lk-pr path { fill: var(--encre); stroke: var(--encre); stroke-width: 48.1; stroke-linejoin: round; }
@@ -237,6 +243,13 @@ html.logo-intro .lk-nom { animation: lk-nom 700ms var(--ease-out) 380ms both; }
@media (max-width: 820px) {
.steps, .promise, .guides { grid-template-columns: 1fr; }
.nav-in nav { display: none; }
/* Téléphone : symbole du logo seul, icône de profil ronde à gauche de « Créer mon arbre », le texte passe en aria-label */
.site-logo .lk-full { display: none; }
.site-logo .lk-mark { display: block; }
.btn.s.nav-login { width: 34px; height: 34px; padding: 0; border-radius: 50%; }
.nav-in { gap: 10px; }
.nav-cta { gap: 6px; }
.nav-login-text { display: none; }
}
@media (max-width: 560px) {
.hero { padding-top: 8px; }