/* Sérène theme — ported from the interactive prototype's design system (docs/superpowers/mockups/serene-v1-prototype.html). */
/* Poppins (SIL OFL, public/fonts/Poppins-OFL.txt), self-hosted: the CSP allows no third-party font host. */
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/poppins-400.woff2") format("woff2"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 500; font-display: swap; src: url("/fonts/poppins-500.woff2") format("woff2"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 600; font-display: swap; src: url("/fonts/poppins-600.woff2") format("woff2"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/poppins-700.woff2") format("woff2"); }
:root {
  color-scheme: light;
  --bg1: #eef5f4; --bg2: #e4eeec; --page: #f1f6f5;
  --card: #fcfcfa; --card-warm: #faf9f5;
  --ink: #1c2b29; --muted: #879593; --muted2: #a7b3b4; --line: #e5eded;
  --teal: #39857b; --teal-deep: #2f6b63; --teal-soft: #6cb7ac; --hero1: #4b9d91; --hero2: #2f6b63;
  --mint: #d3ece8; --mint-soft: #e5f2f0;
  --peach-bg: #f8efee; --peach-ink: #c07862; --cream: #f7efe4; --cream-ink: #b79a5e;
  --grad-soft: linear-gradient(145deg, #bed7d3, #e8cac0); /* avatars / icons: teal -> peach */
  --coral: #e3a08f; --amber: #c1873a; --amber-bg: #f6ecda; --gold: #c9a96e;
  --accent: var(--teal);
  --radius: 20px;
  --shadow: 0 12px 34px rgba(30,70,66,.07), 0 2px 8px rgba(30,70,66,.05);
  --shadow-sm: 0 4px 14px rgba(30,70,66,.06);
  --font: "Poppins", "Segoe UI Variable", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
}
* { box-sizing: border-box; }
html, body { margin: 0; }
/* Form controls don't inherit the page font by default. */
button, input, select, textarea { font-family: inherit; }
body {
  font-family: var(--font);
  color: var(--ink);
  background: radial-gradient(900px 520px at 85% -10%, rgba(190,215,211,.55), transparent 65%),
    radial-gradient(800px 500px at 0% 105%, rgba(232,202,192,.28), transparent 60%), linear-gradient(150deg, var(--bg1), var(--bg2));
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
  min-height: 100vh;
}
.wrap { max-width: 1180px; margin: 0 auto; padding: 24px; }
a { color: var(--accent); }

/* ── Auth (split-gradient), ported from the prototype ── */
.auth { display: grid; grid-template-columns: 0.9fr 1.1fr; min-height: 560px; max-width: 1000px;
  margin: 24px auto; background: var(--card); border-radius: 24px; overflow: hidden;
  box-shadow: 0 20px 60px rgba(16,40,38,.12); }
.auth-side { background: radial-gradient(380px 300px at 78% 22%, rgba(214,178,160,.38), transparent 70%),
  radial-gradient(420px 360px at 12% 88%, rgba(255,255,255,.16), transparent 70%), linear-gradient(160deg, #8dc9c1, #3c887e); color: #fff; padding: 40px 34px;
  display: flex; flex-direction: column; }
.auth-brand { display: flex; align-items: center; gap: 11px; font-weight: 600; font-size: 18px; }
.auth-brand .logo { width: 38px; height: 38px; border-radius: 11px; background: rgba(255,255,255,.16);
  display: grid; place-items: center; font-weight: 600; }
.auth-phrase { font-size: 30px; font-weight: 600; line-height: 1.2; margin-top: auto; }
.auth-form { padding: 40px; display: flex; flex-direction: column; justify-content: center; }
.auth-form h1 { font-size: 26px; margin: 0 0 4px; }
.auth-sub { color: var(--muted); font-size: 14px; margin: 0 0 20px; }
.auth-form label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 14px; }
.auth-form input { display: block; width: 100%; margin-top: 6px; padding: 11px 14px; border: 1px solid var(--line);
  border-radius: 11px; background: #fafcfc; font-size: 15px; font-family: inherit; }
.auth-form input:focus { outline: none; border-color: var(--accent); background: #fff; }
.btn-primary { width: 100%; padding: 13px; border: 0; border-radius: 12px; background: var(--accent); color: #fff;
  font-weight: 600; font-size: 15px; cursor: pointer; margin-top: 4px; }
.auth-err { background: #fdecec; color: #b3261e; border-radius: 11px; padding: 11px 14px; font-size: 13.5px; margin: 0 0 16px; }
.auth-hint { font-size: 12px; color: var(--muted); margin: -8px 0 14px; }
.auth-link-right { text-align: right; margin: -6px 0 16px; font-size: 13.5px; }
.auth-back { margin-top: 16px; text-align: center; font-size: 13.5px; }
.auth-msg { text-align: center; }
.auth-ic { font-size: 44px; }
@media (max-width: 720px) { .auth { grid-template-columns: 1fr; } }

/* ── Onboarding (on top of the auth split-gradient) ── */
.onb-dots { display: flex; gap: 8px; margin-top: 22px; }
.onb-dots i { width: 9px; height: 9px; border-radius: 50%; background: rgba(255,255,255,.3); }
.onb-dots i.on { background: #fff; }
/* .onb-form (the <form>) stretches to the full height of the .auth-form panel; .onb-body
   (title/sub/fields) takes the remaining space and centers its content, which pins .onb-nav
   to the bottom — mirrors the prototype's onb-main(flex:1)/onb-nav(margin-top:auto) split. */
.onb-form { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.onb-body { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.onb-prog { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.onb-hint { font-size: 12px; color: var(--muted); margin: -8px 0 12px; }
.onb-err { color: #b3261e; font-size: 12.5px; margin: -8px 0 12px; }
.onb-row { display: flex; gap: 10px; }
.onb-row > .onb-field { flex: 1; min-width: 0; }
.onb-field { margin-bottom: 13px; }
.onb-height[hidden] { display: none; } /* JS toggles the non-selected height variant (onboarding.js) */
.onb-dob { display: flex; gap: 8px; margin: 6px 0 14px; }
.onb-dob select { flex: 1; padding: 10px; border: 1px solid var(--line); border-radius: 11px; background: #fafcfc; font-family: inherit; }
.onb-chips { display: flex; flex-direction: column; gap: 10px; margin: 6px 0 8px; }
/* Selectors are scoped under their list wrapper (.onb-chips/.onb-rythmes/.onb-consents) so their
   specificity (0,2,0) beats the generic ".auth-form label" caption rule (0,1,1) — these are
   "label wraps a whole selectable control" widgets, not field captions, and need their own color/size. */
.onb-chips .onb-chip { position: relative; display: block; text-align: left; padding: 14px 16px; border-radius: 13px;
  border: 1.5px solid var(--line); background: #fafcfc; font-size: 14.5px; font-weight: 500; color: var(--ink); margin-bottom: 0; cursor: pointer; }
.onb-chips .onb-chip input[type=radio] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.onb-chips .onb-chip:has(input:checked) { border-color: var(--teal); background: var(--mint-soft); color: var(--teal-deep); }
.onb-chips .onb-chip:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.onb-rythmes { display: flex; flex-direction: column; gap: 9px; margin: 6px 0 8px; }
.onb-rythmes .onb-ry { position: relative; display: flex; align-items: center; gap: 12px; border: 1.5px solid var(--line);
  border-radius: 13px; padding: 13px; color: var(--ink); margin-bottom: 0; cursor: pointer; }
.onb-rythmes .onb-ry input[type=radio] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.onb-rythmes .onb-ry:has(input:checked) { border-color: var(--teal); background: var(--mint-soft); }
.onb-ry-dot { width: 20px; height: 20px; border-radius: 50%; border: 2px solid #cdd8d4; flex: 0 0 auto; }
.onb-rythmes .onb-ry:has(input:checked) .onb-ry-dot { border-color: var(--teal); background: var(--teal); box-shadow: inset 0 0 0 3px #fff; }
.onb-rythmes .onb-ry b { display: block; font-size: 14.5px; color: var(--ink); }
.onb-rythmes .onb-ry small { display: block; color: var(--muted); font-size: 12px; }
.onb-rythmes .onb-ry:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.onb-consents { display: flex; flex-direction: column; gap: 12px; margin-top: 18px; }
/* Row wraps the label + the "consulter" link as SIBLINGS (layout only, no click semantics of its
   own) — the link must never be a descendant of .onb-consent, or clicking it would also toggle
   the checkbox (label click-forwarding). */
.onb-consent-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.onb-consents .onb-consent { display: flex; align-items: center; gap: 11px; font-size: 13.5px; color: var(--ink);
  margin-bottom: 0; cursor: pointer; min-width: 0; }
/* Custom checkbox: the tick is a centered background SVG (not ::after — pseudo-elements on form
   controls render inconsistently across browsers, which is why the tick looked misplaced). */
.onb-consent input[type=checkbox] { appearance: none; -webkit-appearance: none; margin: 0; width: 22px; height: 22px;
  border-radius: 7px; border: 2px solid #cdd8d4; flex: 0 0 auto; background: #fff center / 14px 14px no-repeat; cursor: pointer; }
.onb-consent input[type=checkbox]:checked { border-color: var(--teal); background-color: var(--teal);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13l4 4L19 7'/%3E%3C/svg%3E"); }
.onb-consent input[type=checkbox]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.onb-consent-link { flex: 0 0 auto; color: var(--teal-deep); font-size: 12.5px; font-weight: 500; text-decoration: underline; }
.onb-consent-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The consents error must breathe below the last checkbox row (base .onb-err has a negative top margin
   meant for tight field spacing, which glued it to the checkbox). */
.onb-consents + .onb-err { margin-top: 12px; }
.onb-nav { display: flex; justify-content: space-between; align-items: center; margin-top: auto; padding-top: 26px; gap: 12px; }
.onb-nav .btn-primary { width: auto; padding: 12px 24px; margin-top: 0; font-size: 14px; }
.btn-ghost { background: #f1f5f3; color: #5c6b67; padding: 12px 24px; border-radius: 12px; font-weight: 600; font-size: 14px; text-decoration: none; }
.onb-recap { text-align: center; }

/* ── App shell (logged-in chrome): left sidebar + main column, ported from the prototype's
   .app / .side / .nav / .main / .top / .plus. The sidebar carries the brand, the nav, and (a
   deliberate deviation from the prototype, which tucks it in the Profil screen) the logout
   control, so it stays reachable from every authenticated page. ── */
.app { display: grid; grid-template-columns: 232px 1fr; min-width: 0;
  background: radial-gradient(900px 500px at 12% -8%, #f2f8f6, transparent 60%), var(--page);
  border-radius: 26px; overflow: hidden; box-shadow: var(--shadow); border: 1px solid rgba(255,255,255,.7); }

.side { background: var(--card); padding: 22px 16px; display: flex; flex-direction: column; gap: 5px;
  border-right: 1px solid var(--line); }
.side-brand { display: flex; align-items: center; gap: 11px; padding: 6px 8px 20px; }
.side-brand .logo { width: 38px; height: 38px; font-size: 17px; border-radius: 11px; flex: 0 0 auto;
  background: linear-gradient(150deg, var(--hero1), var(--hero2)); color: #fff; font-weight: 600;
  display: grid; place-items: center; }
.side-brand b { display: block; font-size: 16px; font-weight: 600; line-height: 1.15; }
.side-brand small { display: block; color: var(--muted); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; }
.side-nav { display: flex; flex-direction: column; gap: 5px; }
.side-sp { flex: 1; }

.nav-item { display: flex; align-items: center; gap: 11px; padding: 11px 12px; border-radius: 13px;
  color: #5c6b67; font-weight: 500; font-size: 14.5px; text-decoration: none; transition: background .15s; }
.nav-item:hover { background: #f2f7f5; }
.nav-item.is-active { background: var(--mint); color: var(--teal-deep); }
.nav-ic { font-size: 16px; flex: 0 0 auto; }

/* Mobile bottom tab bar — replacement for .side under the mobile breakpoint, ported from the
   prototype's mobileShell() `.tabbar`/`.tab`. Hidden by default (desktop/tablet keep the sidebar);
   the mobile media query below both hides .side and reveals this. */
.mobile-tabbar { display: none; position: fixed; left: 10px; right: 10px; bottom: 10px; z-index: 40;
  background: var(--card); border-radius: 20px; justify-content: space-around; align-items: center;
  padding: 10px 6px; box-shadow: 0 -6px 20px rgba(30,70,66,.08); border: 1px solid var(--line); }
.mobile-tab { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 2px 10px;
  border-radius: 10px; color: var(--muted2); font-weight: 500; font-size: 10px; text-decoration: none; }
.mobile-tab-ic { font-size: 18px; }
.mobile-tab.is-on { color: var(--teal-deep); }
.mobile-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.main { min-width: 0; padding: 26px 30px 40px; }
.main-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 20px; }
.main-title { font-size: 27px; margin: 0 0 3px; letter-spacing: -.01em; }
.main-sub { margin: 0; color: var(--muted); font-size: 14px; }
.plus { width: 42px; height: 42px; border-radius: 13px; background: var(--card); border: 1px solid var(--line);
  display: grid; place-items: center; box-shadow: var(--shadow-sm); color: var(--teal-deep); font-size: 20px;
  line-height: 1; cursor: pointer; font-family: inherit; transition: transform .12s; flex: 0 0 auto; }
.plus:hover { transform: translateY(-1px); }
.plus:focus-visible, .nav-item:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Site footer (legal links), rendered once by render_layout() so every screen gets it ── */
.app-footer { max-width: 1180px; margin: 18px auto 0; padding: 0 24px 20px; }
.legal-links { display: flex; flex-wrap: wrap; gap: 6px 18px; justify-content: center; }
.legal-links a { color: var(--muted); font-size: 12.5px; text-decoration: none; }
.legal-links a:hover, .legal-links a:focus-visible { color: var(--teal-deep); text-decoration: underline; }
.footer-version { text-align: center; color: var(--muted); font-size: 11px; margin: 8px 0 0; }

/* ── Legal doc pages (CGU / confidentialité / avis médical) — placeholder content ── */
.legal-doc { max-width: 640px; margin: 24px auto; background: var(--card); border-radius: 22px;
  padding: 34px 40px; box-shadow: var(--shadow); }
.legal-doc h1 { font-size: 24px; margin: 0 0 14px; }
.legal-placeholder { color: var(--muted); font-size: 14.5px; line-height: 1.6; }
.legal-back { margin-top: 22px; font-size: 13.5px; }

@media (max-width: 860px) {
  .app { grid-template-columns: 1fr; border-radius: 20px; }
  .side { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px; padding: 14px 16px;
    border-right: 0; border-bottom: 1px solid var(--line); }
  .side-brand { padding: 0; margin-right: auto; }
  .side-nav { flex-direction: row; flex-wrap: wrap; gap: 6px; }
  .side-sp { flex-basis: 100%; height: 0; }
  .main { padding: 18px 16px 30px; }
  .main-top { margin-bottom: 14px; }
}

/* ── True mobile chrome (~390-640px): the prototype's mobileShell(), not just a reflowed desktop.
   The sidebar (brand + nav + logout) disappears in favour of a compact header (.main-top, already
   shared with desktop) and the fixed .mobile-tabbar; the app card goes edge-to-edge like the
   prototype's phone screen instead of floating as a framed card. Logout stays reachable from
   Profil (not yet built) — matching the prototype's own mobile behaviour, a deliberate narrowing
   of the desktop-only "logout everywhere" deviation noted above. ── */
@media (max-width: 640px) {
  .wrap:has(> .app) { padding: 0; max-width: none; }
  body:has(.app) .app-footer { padding-bottom: 96px; }
  .app { display: block; border-radius: 0; box-shadow: none; border: 0; min-height: 100vh; }
  .side { display: none; }
  .main { padding: 16px 16px 28px; padding-bottom: calc(28px + 78px); }
  .main-top { margin-bottom: 14px; }
  .main-title { font-size: 21px; }
  .main-sub { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .mobile-tabbar { display: flex; }
}

/* ── Tableau (dashboard), ported from the prototype's .hero / .ringrow / .card / .k / .statbig / .empty ──
   The page is a vertical stack of paired rows (mirrors the prototype's tableau(): a hero/chart row of
   uneven columns, then two even-column rows) — .tab-row-hero / .tab-row-2 do the pairing, .tab-grid is
   just the outer stack. */
.tab-grid { display: flex; flex-direction: column; gap: 18px; }
.tab-row-hero { display: grid; grid-template-columns: minmax(290px, 1fr) 1.5fr; gap: 18px; align-items: stretch; }
.tab-row-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; align-items: stretch; }
.tab-card { background: var(--card); border-radius: var(--radius); padding: 22px; box-shadow: var(--shadow-sm);
  border: 1px solid rgba(255,255,255,.6); min-width: 0; }
.tab-k { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.tab-k-light { color: rgba(255,255,255,.8); }

/* hero */
.tab-hero { background: linear-gradient(155deg, var(--hero1), var(--hero2)); color: #fff; border-radius: var(--radius);
  padding: 24px; display: flex; flex-direction: column; box-shadow: var(--shadow); min-width: 0; }
.tab-hero-top { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 16px; }
.tab-tag { background: rgba(255,255,255,.16); padding: 6px 12px; border-radius: 999px; font-size: 12.5px; font-weight: 500;
  white-space: nowrap; }
.tab-ringrow { display: flex; align-items: center; gap: 18px; }
.tab-ring { flex: 0 0 auto; width: 96px; height: 96px; }
.tab-ring-label { text-transform: uppercase; }
.tab-big { font-size: 38px; font-weight: 600; line-height: 1; letter-spacing: -.02em; }
.tab-rest { font-size: 13px; opacity: .85; margin-top: 6px; }
/* "Prochain jalon" tile (Badges Phase B) — a rounded inner tile inside the hero, ported from the
   prototype's .hero-next/.mtile/.mk/.mv. Sits between the ring row and the hero buttons, filling the
   hero's vertical middle per the locked "no hollow middle" layout decision. */
.tab-hero-next { display: flex; align-items: center; gap: 12px; background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.15); border-radius: 14px; padding: 12px 14px; margin-top: 16px; }
.tab-mtile { width: 38px; height: 38px; border-radius: 11px; background: rgba(255,255,255,.16);
  display: grid; place-items: center; font-size: 19px; flex: 0 0 auto; }
.tab-mk { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; opacity: .8; }
.tab-mv { font-size: 15px; font-weight: 600; margin-top: 2px; }
.tab-mv-sub { font-size: 12px; font-weight: 500; opacity: .68; margin-top: 1px; }
/* As the prototype's .hbtns/.btn: the two buttons share the row equally (flex: 1) at every width; a
   long label ("Ma première pesée" in a half-width day-0 hero) wraps INSIDE its button rather than
   dropping the button to a new line. font-family only is inherited: a `font: inherit` shorthand here
   would reset the 14px / 650 weight. */
.tab-big-unit { font-size: 17px; font-weight: 500; opacity: .9; letter-spacing: 0; } /* prototype .ringrow .big span */
.tab-hero-btns { display: flex; gap: 10px; margin-top: 18px; }
.tab-hero-btn { flex: 1 1 0; min-width: 0; text-align: center; padding: 12px; border-radius: 12px; font-weight: 500;
  font-size: 14px; font-family: inherit; line-height: 1.3; background: #fff; color: var(--teal-deep); border: 1px solid #fff;
  cursor: pointer; text-decoration: none; transition: background .12s; }
.tab-hero-btn:hover { background: #f1f7f6; }
.tab-hero-btn-ic { font-weight: 600; }
/* ghost variant (the "Voir mes besoins" link) — darker translucent fill, no border, ported from
   the prototype's .btn.ghost; same padding/radius/font-weight as the solid pesée button. */
.tab-hero-btn.is-hero-ghost { background: rgba(255,255,255,.2); color: #fff; border-color: transparent;
  display: flex; align-items: center; justify-content: center; } /* a link: centre its label vertically like the <button> beside it */
.tab-hero-btn.is-hero-ghost:hover { background: rgba(255,255,255,.28); }

/* cette semaine — editorial: a big signed number (unit as a small trailing span) + a neutral pace line */
.tab-week .tab-stat { font-size: 38px; font-weight: 600; letter-spacing: -.02em; line-height: 1; margin: 12px 0 0; color: var(--ink); }
.tab-week .tab-stat span { font-size: 16px; font-weight: 500; color: var(--muted); }
.tab-week .tab-stat.is-loss { color: var(--teal-deep); }
.tab-week .tab-stat.is-gain { color: var(--amber); }
.tab-week .tab-stat.is-flat { color: var(--ink); }
.tab-pace { color: var(--muted); font-size: 13px; margin: 8px 0 0; }
.tab-empty { border: 1px dashed #cfdcd8; border-radius: 14px; padding: 22px; text-align: center; color: var(--muted);
  font-size: 14px; background: #f7faf9; margin: 14px 0 0; }
.tab-metric { color: var(--muted); font-size: 13px; margin: 10px 0 0; padding-top: 12px; border-top: 1px solid var(--line); }
.tab-metric.is-muted { color: #a9b6b2; }

/* évolution — inline SVG ported from the prototype's chartSVG(): geometry lives in SVG attributes
   (cx/cy/d/x/y); per-metric COLOR is also a presentation attribute (fill/stroke/stop-color, set by
   the view from chart_series()'s hardcoded hex — not the `style=` attribute CSP restricts, same
   pattern as the hero ring), since it now varies per metric instead of always being teal. */
.tab-chart-svg { display: block; width: 100%; height: auto; margin-top: 12px; }
.tab-chart-grid { stroke: #eef3f1; }
.tab-chart-lbl { font-size: 8px; fill: #b7c3bf; }
.tab-chart-line { fill: none; stroke-width: 1.6px; stroke-linecap: round; stroke-linejoin: round; }
.tab-chart-pt { stroke: #fff; stroke-width: 1.5; cursor: pointer; }
.tab-chart-pt:hover { r: 6; }
.tab-chart-pt.is-last { stroke-width: 2.5; }
.tab-chart-goal { stroke: #e08a6a; stroke-width: 1.4; stroke-dasharray: 5 4; }
.tab-chart-goal-lbl { font-size: 8px; font-weight: 600; fill: #c86b4d; }
.tab-chart-sub { margin: 4px 0 0; color: var(--muted); font-size: 13px; }

/* ⤢ "Agrandir" modal: same .tab-chart-svg (viewBox 0 0 620 210, width:100%/height:auto above) drawn
   into a wider .tab-modal, so it scales up proportionally — no separate SVG geometry/CSS needed.
   Its own chips + range tabs (ported from the prototype's S.modal==='chart') share the card's
   .tab-mchips/.rangetabs styling; .tab-chart-expand-actions just right-aligns the range tabs row,
   matching the prototype's inline `justify-content:flex-end`. */
/* Two-class selector so it beats the base `.tab-modal { max-width }` (declared later, equal
   1-class specificity). The Évolution expand modal is a wide near-full-screen chart on desktop. */
.tab-modal.chart-expand-modal { max-width: min(868px, 94vw); position: relative; }
/* Top-right × close (replaces a bottom "Fermer"), matching the artifact. */
.chart-expand-close { position: absolute; top: 16px; right: 16px; width: 38px; height: 38px; border-radius: 12px;
  border: 1px solid var(--line); background: #fff; color: var(--muted); font-size: 20px; line-height: 1; cursor: pointer;
  display: grid; place-items: center; }
.chart-expand-close:hover { color: var(--ink); background: #f4f7f6; }
.tab-chart-expand-actions { display: flex; justify-content: flex-end; margin-bottom: 8px; }
.chart-expand-slot .tab-chart-svg { margin-top: 8px; }

/* évolution — metric chips + range tabs (Task 2 server baseline; charts.js, Task 3, wires the clicks).
   Ported from the prototype's .mchips/.mchip/.rangetabs (L98-107); dot color is a per-metric class
   (.mdot-KEY), never inline style — strict CSP. */
.tab-mchips { display: flex; gap: 7px; flex-wrap: wrap; margin: 14px 0 2px; }
.mchip { display: inline-flex; align-items: center; gap: 7px; font-family: inherit; font-size: 12.5px; padding: 6px 12px;
  border-radius: 999px; background: #f4f7f6; color: #6c7a76; font-weight: 500; border: 1px solid transparent; cursor: pointer;
  transition: background .15s, border-color .15s; }
.mchip .mdot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; opacity: .5; }
.mchip.is-on { background: #fff; color: var(--teal-deep); border-color: #cfe6e0; box-shadow: var(--shadow-sm); }
.mchip.is-on .mdot { opacity: 1; }
.mdot-poids { background: #2f6d66; }
.mdot-graisse { background: #e08a6a; }
.mdot-muscle { background: #5aa59a; }
.mdot-taille { background: #c9a96e; }
.mdot-pas { background: #7e9bc9; }
.rangetabs { display: flex; gap: 4px; background: #f1f5f3; border-radius: 11px; padding: 4px; flex: 0 0 auto; }
.rangetabs button { font-family: inherit; font-size: 11.5px; padding: 5px 10px; border-radius: 8px; color: var(--muted);
  font-weight: 500; cursor: pointer; }
.rangetabs button.is-on { background: #fff; color: var(--ink); box-shadow: var(--shadow-sm); }

/* shared card header: kicker (± subtitle) on the left, small affordances on the right */
/* Card titles, as the prototype's `.card h3`: no browser-default margin, so a header pill lines up with the title. */
.tab-card h3 { margin: 0 0 3px; font-size: 17px; font-weight: 600; }
.tab-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; flex-wrap: wrap; }
/* Let the title/subtitle keep full width when the card is narrow: the actions drop to their own line
   instead of squeezing the subtitle (e.g. Évolution's "Poids · 8 semaines" beside the range tabs). */
.tab-card-head > :first-child { flex: 1 1 auto; min-width: 0; }
.tab-card-head-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.tab-iconbtn { width: 32px; height: 32px; border-radius: 9px; border: 1px solid var(--line); background: #fff;
  color: var(--muted); display: grid; place-items: center; cursor: pointer; transition: background .12s, color .12s; }
.tab-iconbtn:hover { background: var(--mint-soft); color: var(--teal-deep); }
.tab-iconbtn svg { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.tab-pill { font-size: 12px; font-weight: 600; padding: 5px 11px; border-radius: 999px; background: var(--mint);
  color: var(--teal-deep); white-space: nowrap; text-decoration: none; }

/* hydratation — a big "X,X L" above a discrete row of glass cells (one <i> per glass, `is-full`
   when drunk), ported from the prototype's statbig + glassesHTML(); no width computed inline, so
   no style= anywhere (strict CSP). */
.tab-hydro-icon { font-size: 16px; }
.tab-hydro-liters { font-size: 38px; font-weight: 600; line-height: 1; letter-spacing: -.02em; margin: 12px 0 0; color: var(--ink); }
.tab-hydro-liters-unit { font-size: 16px; font-weight: 500; color: var(--muted); }
.tab-glasses { display: flex; gap: 6px; margin: 14px 0 12px; }
.tab-glass { flex: 1; height: 22px; border-radius: 6px; background: #eef3f1; transition: background .15s; }
.tab-glass.is-full { background: linear-gradient(180deg, #5bb8ae, var(--teal-deep)); }
.tab-hydro-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.tab-hydro-label { margin: 0; color: var(--muted); font-size: 13px; }
.tab-hydro-btns { display: flex; gap: 8px; flex: 0 0 auto; }
.tab-hydro-add, .tab-hydro-sub { width: 40px; height: 36px; border-radius: 10px; border: 1px solid var(--line); background: #fff;
  font-size: 20px; line-height: 1; color: var(--teal-deep); font-weight: 600; cursor: pointer; transition: background .12s; }
.tab-hydro-add:hover, .tab-hydro-sub:hover { background: var(--mint-soft); }

/* habitudes du jour — the whole row is the toggle button; state via .is-checked on the row.
   Per-habit streaks aren't shown per row (the header pill carries the aggregate max instead). */
.tab-habits-sub { margin: 4px 0 0; color: var(--muted); font-size: 13px; }
.tab-habits { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.tab-habit-toggle { display: flex; align-items: center; gap: 12px; width: 100%; padding: 12px; border: 0; border-radius: 12px;
  background: transparent; text-align: left; font: inherit; color: var(--ink); cursor: pointer; transition: background .12s; }
.tab-habit-toggle:hover { background: #f4f8f7; }
.tab-habit.is-checked .tab-habit-toggle { background: var(--mint-soft); }
.tab-cbox { width: 22px; height: 22px; border-radius: 7px; border: 2px solid #cdd8d4; display: grid; place-items: center;
  flex: 0 0 auto; color: #fff; font-size: 13px; font-weight: 600; transition: background .12s, border-color .12s; }
.tab-habit.is-checked .tab-cbox { background: var(--accent); border-color: var(--accent); }
.tab-habit-em { font-size: 16px; flex: 0 0 auto; }
.tab-habit-lab { flex: 1; min-width: 0; font-size: 14px; }
.tab-habit.is-checked .tab-habit-lab { text-decoration: line-through; color: var(--muted); }
.tab-habit-state { font-size: 12px; color: var(--muted); white-space: nowrap; }

/* quick-add menu + modals — ported from the prototype's .menu / .scrim / .modal / .field / .mbtns.
   Hidden state is the `hidden` attribute: these rules set display explicitly, so the [hidden]
   overrides below are required (an author display: beats the UA's [hidden] { display: none }). */
.tab-qa-menu { position: fixed; top: 70px; right: 24px; z-index: 55; background: #fff; border-radius: 16px; padding: 8px;
  box-shadow: 0 20px 50px rgba(16,40,38,.22); border: 1px solid var(--line); min-width: 190px; animation: tab-pop .18s ease; }
.tab-qa-menu button { display: flex; align-items: center; gap: 11px; width: 100%; text-align: left; padding: 11px 12px;
  border: 0; border-radius: 11px; background: transparent; font: inherit; font-size: 14px; font-weight: 500; color: var(--ink); cursor: pointer; }
.tab-qa-menu button:hover, .tab-qa-menu button:focus-visible { background: var(--mint-soft); outline: none; }
.tab-qa-em { font-size: 17px; }

.tab-scrim { position: fixed; inset: 0; z-index: 50; background: rgba(30,45,40,.34); display: flex; align-items: center;
  justify-content: center; padding: 20px; animation: tab-fade .18s ease; }
.tab-modal { background: #fff; border-radius: 22px; padding: 24px; width: 100%; max-width: 420px; max-height: 92vh; overflow-y: auto;
  box-shadow: 0 30px 70px rgba(16,40,38,.3); animation: tab-pop .2s ease; }
.tab-modal h3 { margin: 0 0 4px; font-size: 19px; }
.tab-modal-sub { color: var(--muted); font-size: 13px; margin: 0 0 16px; }
.tab-field { margin-bottom: 13px; min-width: 0; }
.tab-field label { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 5px; font-weight: 500; }
.tab-field input, .tab-field textarea { width: 100%; border: 1px solid var(--line); border-radius: 11px; padding: 11px 12px;
  font: inherit; font-size: 15px; background: #fafcfc; color: var(--ink); outline: none; }
.tab-field textarea { resize: vertical; }
.tab-field input:focus, .tab-field textarea:focus { border-color: #bfe0d8; background: #fff; }
.tab-field input.is-invalid { border-color: #d98c6b; }
.tab-field-row { display: flex; gap: 10px; }
.tab-field-row > .tab-field { flex: 1; }
.tab-more { margin: 2px 0 13px; }
.tab-more summary { cursor: pointer; font-size: 13px; font-weight: 500; color: var(--teal-deep); padding: 6px 0 10px; list-style: none; }
.tab-more summary::-webkit-details-marker { display: none; }
.tab-more summary::before { content: '▸ '; }
.tab-more[open] summary::before { content: '▾ '; }
.tab-modal-err { color: #b8563a; font-size: 13px; margin: 0 0 10px; }
.tab-modal-actions { display: flex; gap: 10px; margin-top: 20px; }
.tab-btn { flex: 1; padding: 12px; border: 0; border-radius: 12px; font: inherit; font-weight: 600; font-size: 14px; cursor: pointer;
  transition: filter .12s, background .12s; }
.tab-btn:hover { filter: brightness(.96); }
.tab-btn.is-ghost { background: #f1f5f3; color: #5c6b67; }
.tab-btn.is-primary { background: var(--accent); color: #fff; }
.tab-btn.is-danger { background: #fbeae3; color: #b8563a; flex: 0 0 auto; }
.tab-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tab-confirm { margin-top: 16px; padding: 14px; border-radius: 14px; background: #fbeae3; }
.tab-confirm p { margin: 0; font-size: 14px; color: #8a3f28; }
.tab-confirm .tab-modal-actions { margin-top: 12px; }
.tab-confirm .tab-btn.is-danger { flex: 1; background: #b8563a; color: #fff; }
/* Account-deletion confirm: the "Supprimer" button must read as truly destructive (solid), not the
   pale reveal variant. Scoped to .delete-confirm so Tableau's shared .tab-btn.is-danger is untouched. */
.delete-confirm .tab-btn.is-danger { flex: 1; background: #b8563a; color: #fff; }
/* Logout form pinned to the bottom of the Profil "Mon compte" card (via profil-fill). */
.profil-logout { margin-top: 14px; }

.tab-qa-menu[hidden], .tab-scrim[hidden], .tab-btn[hidden], .tab-confirm[hidden], .tab-modal-err[hidden] { display: none; }

@keyframes tab-fade { from { opacity: 0; } }
@keyframes tab-pop  { from { transform: translateY(10px) scale(.98); opacity: .6; } }

@media (max-width: 720px) {
  .tab-grid { gap: 14px; }
  .tab-row-hero, .tab-row-2 { grid-template-columns: 1fr; gap: 14px; }
  .tab-card, .tab-hero { padding: 18px; }
  .tab-big, .tab-week .tab-stat, .tab-hydro-liters { font-size: 32px; }
  /* overlays become bottom sheets, like the prototype's phone frame */
  .tab-qa-menu { top: auto; right: 0; left: 0; bottom: 0; border-radius: 20px 20px 0 0; box-shadow: 0 -10px 40px rgba(16,40,38,.2); }
  .tab-scrim { align-items: flex-end; padding: 0; }
  .tab-modal { max-width: none; border-radius: 22px 22px 0 0; max-height: 90vh; box-shadow: 0 -10px 40px rgba(16,40,38,.28); animation: tab-sheet .28s ease; }
  .tab-modal.chart-expand-modal { max-width: none; } /* keep the full-width bottom sheet on mobile */
  /* The Évolution card is already full-width + interactive on mobile, so "expand" only duplicates it
     — hide the ⤢ affordance here; it returns on wider (desktop / landscape >720px) viewports. */
  [data-qa="chart-expand"] { display: none; }
  .tab-field-row { flex-direction: column; gap: 0; }
}
@keyframes tab-sheet { from { transform: translateY(28px); opacity: .5; } }

/* ── Calculs / Mes besoins ── */
.calc-grid { display: flex; flex-direction: column; gap: 18px; }

/* hero: cible quotidienne + métabolisme/dépense breakdown, mirrors .tab-hero's chrome */
.calc-hero { background: linear-gradient(155deg, var(--hero1), var(--hero2)); color: #fff; border-radius: var(--radius);
  padding: 24px; display: flex; flex-direction: column; box-shadow: var(--shadow); min-width: 0; }
.calc-hero-top { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 16px; }
.calc-tag { background: rgba(255,255,255,.16); padding: 6px 12px; border-radius: 999px; font-size: 12.5px; font-weight: 500;
  color: #fff; white-space: nowrap; }
.calc-tag.is-btn { border: 0; font-family: inherit; cursor: pointer; transition: background .12s; }
.calc-tag.is-btn:hover { background: rgba(255,255,255,.26); }
.calc-tag.is-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.calc-hero-k { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; font-weight: 600; color: rgba(255,255,255,.85); }
.calc-hero-big { font-size: 52px; font-weight: 600; line-height: 1; letter-spacing: -.02em; margin: 6px 0 4px; }
.calc-hero-big span { font-size: 18px; font-weight: 500; opacity: .85; }
.calc-hb { margin-top: 14px; padding-top: 4px; border-top: 1px solid rgba(255,255,255,.18); }
.calc-hb-row { display: flex; justify-content: space-between; align-items: center; gap: 14px; padding: 11px 0;
  border-bottom: 1px solid rgba(255,255,255,.12); }
.calc-hb-row:last-child { border-bottom: 0; }
.calc-hb-l { font-size: 13.5px; font-weight: 600; }
.calc-hb-x { font-size: 11.5px; opacity: .72; margin-top: 2px; line-height: 1.4; }
.calc-hb-v { font-size: 20px; font-weight: 600; white-space: nowrap; }

/* macronutriments — SVG progress bars (CSP-safe: geometry via attrs, color via these classes) */
.calc-macro + .calc-macro { margin-top: 15px; }
.calc-macro-row { display: flex; justify-content: space-between; align-items: baseline; font-size: 14px; margin-bottom: 7px; gap: 10px; }
.calc-macro-row em { font-style: normal; color: var(--muted); white-space: nowrap; }
.calc-bar { display: block; width: 100%; height: 6px; }
.calc-bar-bg { fill: var(--line); }
.calc-bar-fill { fill: var(--teal); }
.calc-bar-fill.is-prot { fill: #2f6d66; }
.calc-bar-fill.is-carb { fill: #6bbfb8; }
.calc-bar-fill.is-fat { fill: #e08a6a; }
.calc-note { margin: 10px 0 0; padding-top: 12px; border-top: 1px solid var(--line); }

/* IMC & poids de santé — value + zone, then the healthy-range gauge (SVG, same CSP approach) */
.calc-imc-top { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-top: 6px; }
.calc-imc-val { font-size: 38px; font-weight: 600; letter-spacing: -.02em; line-height: 1; color: var(--ink); }
.calc-imc-zone { font-size: 13px; font-weight: 600; color: var(--teal-deep); min-width: 0; }
.calc-imc.is-amber .calc-imc-zone { color: var(--amber); }
.calc-gauge { display: block; width: 100%; height: 12px; margin-top: 14px; }
.calc-gauge-track { fill: url(#calc-imc-grad); }
.calc-gauge-needle { fill: var(--teal); stroke: #fff; stroke-width: 1.5; }
.calc-gscale { display: flex; justify-content: space-between; color: var(--muted); font-size: 11px; margin-top: 6px; }

/* activité — reuses .calc-imc-val for the steps figure, .calc-unit for its trailing "pas/j";
   its "Ajuster" affordance is a <button class="tab-pill is-btn">, so reset native button chrome. */
.calc-unit { font-size: 16px; font-weight: 500; color: var(--muted); }
.tab-pill.is-btn { border: 0; font-family: inherit; cursor: pointer; transition: background .12s; }
.tab-pill.is-btn:hover { background: var(--mint); filter: brightness(.97); }
.tab-pill.is-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* two-column hero row: déficit hero + macros card */
.calc-grid-hero { display: grid; grid-template-columns: minmax(290px, 1fr) 1.5fr; gap: 18px; }

/* fibres row, closes out the macros card */
.calc-fiber { display: flex; justify-content: space-between; align-items: center; font-size: 14px; margin-top: 15px;
  padding-top: 14px; border-top: 1px solid var(--line); }
.calc-fiber em, .calc-fiber .val { font-style: normal; color: var(--muted); }

/* coach card — warm background lives on the card itself (section.tab-card.calc-coach) */
.calc-coach { display: flex; gap: 13px; background: var(--card-warm); }
.calc-coach-av { width: 34px; height: 34px; border-radius: 11px; background: var(--grad-soft);
  flex: 0 0 auto; }
.calc-coach-txt { flex: 1; min-width: 0; }
.calc-coach-who { font-weight: 600; font-size: 14px; margin-bottom: 3px; }
.calc-coach-txt p { margin: 0; font-size: 14px; color: #485652; }
.calc-coach-txt p b { color: var(--ink); }

/* reminder CTA — gentle pill under a rappels-doux nudge in the coach card (Wave 1 Task 2) */
.tab-coach-cta { display: inline-block; margin-top: 10px; padding: 7px 14px; border-radius: 999px;
  border: 1px solid var(--teal); background: #fff; color: var(--teal-deep); font-weight: 600;
  font-size: 13.5px; text-decoration: none; cursor: pointer; }
.tab-coach-cta:hover { background: var(--mint-soft); }

/* pistes — optional tips under the Tableau coach message, ported from the prototype's
   .pistes/.piste/.pistes-intro/.piste-note (never shown for the 'off' coach state). */
.tab-pistes { margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(16,40,38,.08); }
.tab-pistes-intro { font-size: 13px; color: var(--muted); font-weight: 500; margin: 0 0 2px; }
.tab-piste { display: flex; gap: 11px; align-items: flex-start; padding: 10px 0; border-top: 1px solid rgba(16,40,38,.06); }
.tab-pistes .tab-piste:first-of-type { border-top: none; padding-top: 6px; }
.tab-piste-em { font-size: 19px; line-height: 1.2; flex: 0 0 auto; }
.tab-piste b { display: block; font-size: 14px; margin-bottom: 2px; color: var(--ink); }
.tab-piste small { display: block; color: var(--muted); font-size: 12.5px; line-height: 1.5; }
.tab-piste-note { font-size: 12px; color: var(--muted); font-style: italic; margin: 10px 0 0; }

/* IMC — two columns: left = value/zone/gauge/scale, right = healthy-zone target + message */
.calc-imc-cols { display: flex; gap: 26px; flex-wrap: wrap; align-items: center; margin-top: 6px; }
.calc-imc-left { flex: 0 0 auto; min-width: 280px; }
.calc-imc-right { flex: 1; min-width: 240px; }
.calc-imc-target { font-size: 15px; font-weight: 600; margin: 4px 0 8px; }

/* 3-col row: activité / hydratation / perte estimée — equal height, like the Tableau rows */
.calc-cards-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 18px; align-items: stretch; margin-top: 18px; }
/* the shorter cards (hydratation, perte) spread head / figure / notes over the stretched height */
.calc-cards-3 > .tab-card { display: flex; flex-direction: column; justify-content: space-between; }
.calc-hydro-ic { color: #7fb7b2; }

@media (max-width: 720px) {
  .calc-hero { padding: 18px; }
  .calc-hero-big { font-size: 40px; }
  .calc-grid-hero, .calc-cards-3 { grid-template-columns: 1fr; }
  .calc-imc-left, .calc-imc-right { min-width: 0; flex-basis: 100%; }
}

/* ── Profil ── */
/* avatar: default (used standalone in the edit modal) + the bigger .profcard variant */
.avatar { width: 56px; height: 56px; border-radius: 18px; flex: 0 0 auto;
  background: var(--grad-soft); color: #fff;
  display: grid; place-items: center; font-weight: 600; font-size: 22px; }
.tab-modal .avatar { margin: 0 auto 4px; }

/* An uploaded avatar (a real <img>) fills the same box as the initial-letter fallback (a <div>). */
img.avatar { object-fit: cover; }

/* Modifier-mon-profil avatar block: preview + upload/remove controls, sits above the profile-edit form. */
.tab-avatar-block { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-bottom: 18px; text-align: center; }
.avatar-upload-form, .avatar-remove-form { margin: 0; }

/* Visually hidden but still keyboard-focusable (clip, not display:none) — the avatar file input
   lives behind the clickable avatar label. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* The avatar preview IS the picker: click the photo/initial to change it (a 📷 badge hints at it).
   The badge is positioned on the picture wrapper (.avatar-edit-pic), not the whole label, so it sits
   on the avatar's corner rather than over the hint text below. */
.avatar-edit { display: inline-flex; flex-direction: column; align-items: center; cursor: pointer; }
.avatar-edit-pic { position: relative; display: inline-block; }
.avatar-edit-pic .avatar { margin: 0; }
.avatar-edit-badge { position: absolute; right: -3px; bottom: -3px; width: 26px; height: 26px;
  border-radius: 50%; background: var(--accent); color: #fff; font-size: 13px; line-height: 1;
  display: grid; place-items: center; border: 2px solid #fff; box-shadow: 0 2px 6px rgba(30,70,66,.18); }
.avatar-edit-hint { display: block; margin-top: 10px; font-size: 13px; font-weight: 500; color: var(--accent); }
.avatar-edit:hover .avatar { filter: brightness(.94); }
.avatar-edit:focus-within .avatar-edit-pic { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 20px; }

/* "Retirer la photo": a quiet text link, not a full button (it was too loud). */
.avatar-remove-link { background: none; border: 0; padding: 0; font: inherit; font-size: 12.5px;
  color: var(--muted); text-decoration: underline; cursor: pointer; }
.avatar-remove-link:hover { color: #b8563a; }
.avatar-note { font-size: 12px; }

/* Native file picker button in the "Ajouter une photo" modal — dress the browser's default chrome
   to read like a Sérène ghost button instead of the raw "Choose file". */
[data-qa="modal-photo-add"] input[type="file"] { max-width: 100%; font-size: 13px; color: var(--muted); }
[data-qa="modal-photo-add"] input[type="file"]::file-selector-button { font: inherit; font-weight: 500;
  cursor: pointer; padding: 9px 14px; margin-right: 10px; border: 1px solid var(--line);
  border-radius: 10px; background: #f1f5f3; color: #5c6b67; }
[data-qa="modal-photo-add"] input[type="file"]::file-selector-button:hover { filter: brightness(.97); }

/* Profil card (row 1, left) */
.profcard { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 2px; }
.profcard .avatar { width: 78px; height: 78px; border-radius: 22px; font-size: 30px; }
.profcard b { font-size: 19px; margin-top: 6px; }
.profcard .em { color: var(--muted); font-size: 13px; }
.editbtn { margin-top: 10px; border: 0; background: var(--mint); color: var(--teal-deep); padding: 8px 18px;
  border-radius: 999px; font-weight: 500; font-size: 13.5px; font-family: inherit; cursor: pointer; transition: filter .12s; }
.editbtn:hover { filter: brightness(.97); }
.editbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Ton parcours (row 1, right) — mint/peach stat boxes, mirrors .tab-k typography for .sbox .k */
.statgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 14px; }
.sbox { border-radius: 16px; padding: 16px; min-width: 0; }
.sbox.mint { background: var(--mint-soft); }
.sbox.peach { background: var(--peach-bg); }
.sbox .k { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); font-weight: 600; margin-bottom: 8px; }
.sbox .v { font-size: 26px; font-weight: 600; letter-spacing: -.02em; }
.sbox.peach .v { color: var(--peach-ink); }

/* row 2: Préférences / Mon compte / Sérène, 3-up like the Calc 3-card row */
.tab-row-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; align-items: stretch; }

/* Equal-height rows leave shorter cards top-aligned with pooled bottom whitespace (Ton parcours,
   Mon compte, Sérène — never the tall Préférences card that sets the row height, nor the centered
   .profcard). Distribute their content top-to-bottom instead, exactly like .calc-cards-3 > .tab-card
   on Mes besoins — heading/subtitle stay at top, stats/links/version get pushed to fill. Scoped to
   this class (not .tab-row-hero/.tab-row-3's align-items, which Tableau shares and must stay stretched). */
.tab-card.profil-fill { display: flex; flex-direction: column; justify-content: space-between; }

/* Préférences — one .toggle per row: icon + label/state text + its own <form> (switch or 2-btn choice) */
.toggle { display: flex; align-items: center; gap: 12px; padding: 13px 0; }
.toggle .ic { width: 36px; height: 36px; border-radius: 11px; background: var(--mint-soft); flex: 0 0 auto;
  display: grid; place-items: center; font-size: 16px; }
.toggle .tx { flex: 1; min-width: 0; }
.toggle b { font-size: 14px; }
.toggle small { display: block; color: var(--muted); font-size: 12px; }
.toggle form { display: flex; flex: 0 0 auto; margin: 0; }

/* on/off switch (server sets the "off" class on the button — no JS) */
.sw { width: 46px; height: 27px; border-radius: 999px; background: var(--teal); border: 0; padding: 0;
  position: relative; flex: 0 0 auto; cursor: pointer; transition: background .15s; }
.sw::after { content: ""; position: absolute; top: 3px; left: 22px; width: 21px; height: 21px; border-radius: 50%;
  background: #fff; transition: left .15s; }
.sw.off { background: #d5ddda; }
.sw.off::after { left: 3px; }
.sw:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sw:disabled { opacity: .45; cursor: not-allowed; }

/* Notifications (push) opt-in block, appended inside the Rappels doux card — separator off the
   toggle rows above it, then a sub-label + muted helper text + a pill CTA matching .editbtn's shape
   but in the app's primary teal (an enable/disable action, not a secondary edit link). */
.pr-push { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.pr-push-head { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
  font-weight: 600; margin-bottom: 6px; }
.pr-push-msg { margin: 0 0 10px; color: var(--muted); font-size: 13px; }
.pr-push-btn { border: 0; background: var(--accent); color: #fff; padding: 9px 18px; border-radius: 999px;
  font-weight: 500; font-size: 13.5px; font-family: inherit; cursor: pointer; transition: filter .12s; }
.pr-push-btn:hover { filter: brightness(.96); }
.pr-push-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* unit (kg/lb) and langue (FR/EN) two-button choice — the active value gets .on */
.langsel { display: flex; gap: 6px; background: #f1f5f3; border-radius: 11px; padding: 4px; flex: 0 0 auto; }
/* .langsel is reused by Journal's Semaine/Mois toggle as <a> links (a GET, not a mutation) */
.langsel button, .langsel a { border: 0; background: transparent; font-family: inherit; font-size: 12.5px; padding: 6px 12px;
  border-radius: 8px; color: var(--muted); font-weight: 500; cursor: pointer; text-decoration: none; }
.langsel button.on, .langsel a.on { background: #fff; color: var(--ink); box-shadow: var(--shadow-sm); }
.langsel button:focus-visible, .langsel a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Mon compte — full-width tappable rows (button or <a>), label + chevron */
.rowlink { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 15px 4px;
  border: 0; border-bottom: 1px solid var(--line); background: transparent; font: inherit; color: inherit;
  text-align: left; text-decoration: none; cursor: pointer; }
.rowlink:last-child { border-bottom: 0; }
.rowlink:hover b { color: var(--teal-deep); }
.rowlink b { font-size: 14.5px; }
.rowlink small { display: block; color: var(--muted); font-size: 12.5px; }
.rowlink .chev { color: var(--muted2); }
.rowlink:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rowlink.is-danger b { color: #b8563a; }

/* Mes données modal — divider before the destructive delete row, warning box in the confirm modal */
.data-div { border: 0; border-top: 1px solid var(--line); margin: 16px 0; }
/* Mes données modal, as the prototype's `data` modal: mint full-width export, "Zone sensible" label,
   soft-pink full-width delete opener, Fermer at the bottom. */
.data-export-btn { display: block; width: 100%; background: var(--mint); color: var(--teal-deep); }
.data-note { margin: 8px 0 0; }
.data-zone { color: #b5443a; }
.data-delete-btn { display: block; width: 100%; margin-top: 10px; background: #f6e3e0; color: #b5443a; }
.delete-warnbox { background: #fbeae3; color: #8a3f28; border-radius: 12px; padding: 12px 14px; font-size: 13px; margin: 12px 0; }

/* Sérène card — warm background, reuses .tab-card's own padding/shadow */
.tab-card.warm { background: var(--card-warm); }

/* Historique du poids / des mesures — read-only aligned-column rows */
.htable { display: flex; flex-direction: column; margin-top: 14px; }
.htr { display: grid; grid-template-columns: 1.3fr 1fr 1fr 1fr; gap: 10px; align-items: center; padding: 11px 6px;
  border-bottom: 1px solid var(--line); font-size: 14px; color: var(--ink); }
.htr.mes { grid-template-columns: 1.3fr 1fr; }
.htr:last-child { border-bottom: 0; }
.htr.hh { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.htr span:first-child { font-weight: 500; }

/* FAQ accordion — native <details>/<summary>, CSS-only +/− marker (no JS) */
.faq { margin-top: 4px; }
.faq-group { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); font-weight: 600;
  margin: 18px 0 4px; }
.faq-group:first-child { margin-top: 4px; }
.faqitem { border-bottom: 1px solid var(--line); }
.faqitem:last-child { border-bottom: 0; }
.faqitem summary { position: relative; list-style: none; cursor: pointer; padding: 13px 26px 13px 0;
  font-size: 14px; font-weight: 500; color: var(--ink); }
.faqitem summary::-webkit-details-marker { display: none; }
.faqitem summary::after { content: '+'; position: absolute; top: 50%; right: 2px; transform: translateY(-50%);
  color: var(--teal-deep); font-weight: 600; font-size: 17px; }
.faqitem details[open] summary::after { content: '−'; }
.faqitem summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.faqitem p { margin: 0 0 14px; color: var(--muted); font-size: 13.5px; line-height: 1.55; }

/* edit-profil modal: read-only fields (email, poids de départ) */
.tab-field input:disabled { background: #f1f5f3; color: var(--muted); cursor: not-allowed; }

/* Below ~1100px, three toggle-heavy cards side by side get too narrow (labels collide with the
   kg/lb + FR/EN controls). Stack them full-width instead — each card then has room for its rows
   on one line, and nothing is forced to an uneven equal height. */
@media (max-width: 1100px) {
  .tab-row-3 { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  .tab-row-3 { grid-template-columns: 1fr; gap: 14px; }
  .htr { grid-template-columns: 1.1fr 1fr 1fr 1fr; gap: 6px; padding: 10px 4px; font-size: 12.5px; }
}

/* Photos de progression -- ported from the prototype's .photorow/.photo/.ph-img/.ph-cap/.ph-del
   (inline styles there, real classes here: strict CSP forbids style=). .photo is used as both a
   <div> (a real tile) and a <button> (the trailing "add" tile) -- the button-chrome reset below
   makes both render identically. */
.photorow { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.photo { position: relative; width: 118px; border-radius: 14px; overflow: hidden; background: #fff;
  border: 1px solid var(--line); text-align: left; padding: 0; margin: 0; font: inherit; color: inherit; }
.photo.add { cursor: pointer; }
.ph-del { position: absolute; top: 6px; right: 6px; width: 24px; height: 24px; border-radius: 50%;
  background: rgba(255,255,255,.92); border: 1px solid var(--line); color: #b3261e; font-size: 15px;
  line-height: 1; display: grid; place-items: center; z-index: 2; cursor: pointer; }
.photo .ph-img { width: 100%; height: 104px; display: block; object-fit: cover;
  background: linear-gradient(160deg, #eef4f2, #e7eef4); }
.photo.add .ph-img { display: grid; place-items: center; font-size: 30px; font-weight: 600;
  color: var(--teal); background: #fafcfc; }
.photo .ph-cap { padding: 8px 10px; }
.photo .ph-cap b { display: block; font-size: 13px; }
.photo .ph-cap small { display: block; color: var(--muted); font-size: 12px; }
/* The thumbnail+caption is a button (opens the full-size viewer); reset its button chrome so the
   tile looks identical to before. The delete "×" sits above it (z-index) and stays clickable. */
.ph-view { display: block; width: 100%; border: 0; padding: 0; margin: 0; background: none;
  font: inherit; color: inherit; text-align: left; cursor: pointer; }
.ph-view:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Full-size progress-photo viewer (a .tab-modal). */
.photo-view-modal { max-width: 520px; }
.photo-view-img { display: block; width: 100%; max-height: 68vh; object-fit: contain; border-radius: 12px;
  background: #f3f7f5; }
.photo-view-cap { text-align: center; margin: 12px 0 0; font-size: 14px; }
.photo-view-cap span { color: var(--muted); margin-left: 8px; }

/* One-shot error banner for a rejected avatar/photo upload (native forms 303 back to /profil).
   Same look as .auth-err so errors read consistently across the app. */
/* Re-auth password (export / delete): breathing room from the button above, as in the prototype. */
.reauth-field { margin-top: 14px; }
.profil-flash { background: #fdecec; color: #b3261e; border-radius: 11px; padding: 11px 14px;
  font-size: 13.5px; font-weight: 500; margin: 0 0 16px; }

/* ── Journal ── */
/* row modifier: natural height instead of stretch, for Focus/Ce-que-j'ai-compris/Victoires and the archive's 2-up cards */
.tab-row-3.start, .tab-row-2.start { align-items: start; }

/* En bref — recap tiles, Semaine/Mois toggle reuses .langsel (see above) */
.jrn-bref-tiles { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 12px; }
.wbrief { flex: 1; min-width: 120px; background: var(--mint-soft); border-radius: 14px; padding: 14px; }
.wv { font-size: 24px; font-weight: 600; letter-spacing: -.01em; color: var(--ink); }
.wv.is-loss { color: var(--teal-deep); }
.wv.is-gain { color: var(--amber); }
.wl { font-size: 11.5px; color: var(--muted); margin-top: 3px; font-weight: 500; }
.jrn-bref-month-sub { margin: 2px 0 0; color: var(--muted); font-size: 13px; }
.jrn-bref-warm { margin: 14px 0 0; line-height: 1.5; color: var(--muted); font-size: 13px; }

/* Comment tu te sens — full-width mood strip, each button its own tiny form */
.jrn-mood-card { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.moods { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.moods form { display: contents; }
.mood { flex: 0 0 auto; min-width: 84px; display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 9px 4px; border: 1px solid transparent; border-radius: 12px; background: #f4f7f6; font-size: 20px;
  font-family: inherit; cursor: pointer; }
.mood span { font-size: 10px; color: var(--muted); font-weight: 500; }
.mood:hover { background: var(--mint-soft); }
.mood.on { background: var(--mint-soft); border-color: #cfe6e0; }
.mood.on span { color: var(--teal-deep); }
.mood:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Habitudes de la semaine — read-only weekly grid, one row per habit */
.wk { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.wk:last-child { margin-bottom: 0; }
.wk .em { font-size: 18px; flex: 0 0 auto; }
.wk .nm { flex: 1; min-width: 0; font-size: 14px; }
.wk .days { display: flex; gap: 4px; flex: 0 0 auto; }
.day { width: 30px; height: 22px; border-radius: 6px; background: #eef3f1; display: grid; place-items: center;
  font-size: 10px; color: var(--muted2); position: relative; }
.day.on { background: linear-gradient(180deg, #5bb8ae, var(--teal-deep)); color: #fff; }
.day.is-today { font-weight: 600; }
.day.is-today::after { content: ""; position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%);
  width: 5px; height: 5px; border-radius: 50%; background: var(--teal); }
.cnt { font-size: 12px; color: var(--muted); width: 26px; text-align: right; flex: 0 0 auto; }

/* Focus de la semaine — checkbox toggle + tap-to-edit <details> + delete, no JS */
.focus { display: flex; align-items: center; gap: 11px; padding: 9px 4px; }
.focus form { display: contents; }
.cbox { width: 20px; height: 20px; border-radius: 7px; border: 2px solid #cdd8d4; display: grid; place-items: center;
  flex: 0 0 auto; color: #fff; font-size: 12px; font-weight: 600; background: transparent; cursor: pointer; padding: 0; }
.focus.done .cbox { background: var(--teal); border-color: var(--teal); }
.jrn-focus-edit { flex: 1; min-width: 0; }
.jrn-focus-edit summary { list-style: none; cursor: pointer; font-size: 14px; color: var(--ink); }
.jrn-focus-edit summary::-webkit-details-marker { display: none; }
.focus.done .jrn-focus-edit summary { text-decoration: line-through; color: var(--muted); }
.jrn-editrow { display: flex; gap: 8px; margin-top: 8px; }
.jrn-editrow input { flex: 1; border: 1px solid var(--line); border-radius: 11px; padding: 8px 10px; font-size: 13px;
  font-family: inherit; background: #fafcfc; outline: none; }
.jrn-editrow input:focus { border-color: #bfe0d8; }
.focus .x, .vic .x { border: 0; background: transparent; color: var(--muted2); font-size: 16px; padding: 2px 6px;
  border-radius: 8px; cursor: pointer; font-family: inherit; }
.focus .x:hover, .vic .x:hover { background: #f0e5e2; color: var(--peach-ink); }
.addrow { display: flex; gap: 8px; margin-top: 10px; }
.addrow input { flex: 1; min-width: 0; border: 1px solid var(--line); border-radius: 11px; padding: 10px 12px; font-size: 13px;
  font-family: inherit; background: #fafcfc; outline: none; }
.addrow input:focus { border-color: #bfe0d8; }
.addbtn { width: 40px; border: 0; border-radius: 11px; background: var(--teal); color: #fff; font-size: 18px;
  cursor: pointer; align-self: stretch; }

/* Ce que j'ai compris — textarea + an explicit save button (the CSP-safe autosave-on-blur equivalent) */
.jrn-textarea { display: block; width: 100%; border: 1px solid var(--line); border-radius: 12px; padding: 12px;
  color: #485652; font-size: 14px; font-family: inherit; background: #fafcfc; min-height: 96px; resize: vertical; }
.jrn-textarea:focus { border-color: #bfe0d8; background: #fff; outline: none; }
.jrn-save-btn { margin-top: 10px; width: auto; padding: 10px 20px; flex: 0 0 auto; }

/* Tes victoires */
.vic { display: flex; align-items: center; gap: 9px; background: var(--mint-soft); border-radius: 11px;
  padding: 11px 13px; font-size: 13.5px; margin-top: 9px; }
.vic .t { flex: 1; min-width: 0; }

/* Semaines précédentes — archive, read-only */
.jrn-archive-title { margin: 26px 0 12px; font-size: 17px; }
.jrn-archive-week { font-size: 15px; margin: 0; }
.jrn-archive-mood { font-size: 24px; }
.jrn-archive-refl { font-size: 14px; color: #485652; margin: 4px 0 12px; line-height: 1.5; }

@media (max-width: 640px) {
  .wk { display: grid; grid-template-columns: auto 1fr auto; grid-template-areas: "em nm cnt" "days days days";
    align-items: center; column-gap: 10px; row-gap: 8px; }
  .wk .em { grid-area: em; } .wk .nm { grid-area: nm; } .wk .cnt { grid-area: cnt; } .wk .days { grid-area: days; }
  .wk .days .day { flex: 1; width: auto; min-width: 0; }
  .moods { flex-wrap: nowrap; justify-content: space-between; width: 100%; }
  .mood { flex: 1; min-width: 0; padding: 9px 2px; }
  .mood span { font-size: 9.5px; }
}

/* ── Admin surface: own light chrome (top bar), NOT the member app shell or the auth split-gradient ── */
.admin-shell { background: var(--card); border-radius: 26px; overflow: hidden; box-shadow: var(--shadow);
  border: 1px solid rgba(255,255,255,.7); max-width: 1180px; margin: 0 auto; }
.admintop { display: flex; align-items: center; gap: 11px; padding: 16px 24px; border-bottom: 1px solid var(--line); }
.admintop .logo { width: 34px; height: 34px; flex: 0 0 auto; border-radius: 10px; font-size: 15px;
  background: linear-gradient(150deg, var(--hero1), var(--hero2)); color: #fff; display: grid; place-items: center; font-weight: 600; }
.admintop b { font-size: 16px; }
.admin-tag { font-size: 10px; font-weight: 600; letter-spacing: .11em; text-transform: uppercase; color: var(--teal-deep);
  background: var(--mint-soft); padding: 3px 8px; border-radius: 6px; }
.admin-sp { flex: 1; }
/* Coach name + buttons stay together and right-aligned; on narrow phones they wrap as one row. */
.admintop { flex-wrap: wrap; }
.admin-actions { display: flex; align-items: center; gap: inherit; margin-left: auto; }
.admintop .abtn { white-space: nowrap; }
.admin-coach { font-size: 13px; color: var(--muted); }
.admin-logout-form { display: contents; }
.abtn { font-size: 12.5px; font-weight: 500; padding: 6px 11px; border-radius: 8px; border: 1px solid var(--line);
  background: #fff; color: var(--teal-deep); cursor: pointer; font-family: inherit; text-decoration: none; display: inline-block; }
.admin-body { padding: 22px 24px; }
.admin-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.admin-head h1 { font-size: 24px; margin: 0; letter-spacing: -.01em; }
.admin-head .sub { color: var(--muted); font-size: 13px; margin: 4px 0 0; }
.admin-flash { background: var(--mint-soft); color: var(--teal-deep); border-radius: 11px; padding: 11px 14px;
  font-size: 13.5px; font-weight: 500; margin: 0 0 16px; }
/* A refusal (e.g. invite rate limit): same look as .auth-err / .profil-flash so errors read consistently. */
.admin-flash.is-err { background: #fdecec; color: #b3261e; }
.utable { width: 100%; border-collapse: collapse; font-size: 14px; }
.utable th { text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted);
  font-weight: 600; padding: 9px 12px; border-bottom: 1px solid var(--line); }
.utable td { padding: 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.utable .un { font-weight: 500; }
.utable .ue { color: var(--muted); font-size: 13px; }

/* Member identity chip (avatar + name) in the admin list — helps the coach recognise who is who. */
.uname { display: flex; align-items: center; gap: 10px; }
.uavatar { width: 32px; height: 32px; border-radius: 10px; flex: 0 0 auto; object-fit: cover;
  background: linear-gradient(150deg, var(--hero1), var(--hero2)); color: #fff;
  display: inline-grid; place-items: center; font-weight: 600; font-size: 13px; }
.sb { font-size: 11px; font-weight: 600; padding: 3px 10px; border-radius: 20px; white-space: nowrap; }
.sb.active { background: var(--mint-soft); color: var(--teal-deep); }
.sb.pending { background: #fff4e0; color: #9a6a12; }
.sb.suspended { background: #fdecec; color: #b3261e; }
.nav-item.admin-link { border-top: 1px solid var(--line); margin-top: 6px; padding-top: 14px; color: var(--teal-deep); }
.admin-row-mobile { display: none; }
@media (max-width: 640px) {
  .admin-shell { border-radius: 0; box-shadow: none; border: 0; }
  .admintop { padding: 14px 16px; gap: 9px; }
  .admintop b { font-size: 15px; }
  .admin-coach { display: none; }
  .admin-body { padding: 18px 16px; }
  .admin-row-mobile { display: flex; }
}

/* ── Admin surface: toolbar / sortable headers / mobile cards / pagination / row actions (Task 4) ── */
.abtn.primary { background: var(--teal); color: #fff; border-color: var(--teal); padding: 9px 16px; font-size: 13.5px; }
.abtn.warn { color: #b3261e; }
.arow { display: flex; gap: 7px; flex-wrap: wrap; }
.arow form { display: contents; }
.utable th.s { cursor: pointer; user-select: none; white-space: nowrap; }
.utable th.s:hover { color: var(--teal-deep); }
.sort-arrow { display: inline-block; min-width: 10px; }
.admin-tools { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.adm-search { flex: 1; min-width: 200px; }
.adm-search input { width: 100%; padding: 10px 14px; border: 1px solid var(--line); border-radius: 11px;
  background: #fafcfc; font-size: 14px; font-family: inherit; color: var(--ink); }
.adm-search input:focus { outline: none; border-color: var(--teal); background: #fff; }
.fpills { display: flex; gap: 6px; flex-wrap: wrap; }
.fpill { font-size: 12.5px; padding: 6px 12px; border-radius: 20px; border: 1px solid var(--line); background: #fff;
  color: var(--muted); font-weight: 500; cursor: pointer; font-family: inherit; }
.fpill.is-on { border-color: var(--teal); background: var(--mint-soft); color: var(--teal-deep); }
.admin-empty { padding: 36px 12px; text-align: center; color: var(--muted); font-size: 14px; }
.msort { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.msort > span { font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; font-weight: 600; margin-right: 2px; }
.ucard { border: 1px solid var(--line); border-radius: 14px; padding: 14px; margin-bottom: 10px; background: #fff; }
.ucard-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; margin-bottom: 8px; }
.ucard .ue { font-size: 13px; color: var(--muted); margin: 1px 0 0; }
.ucard .ud { font-size: 12px; color: var(--muted); margin: 0 0 10px; }
.apag { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); }
.apinfo { font-size: 12.5px; color: var(--muted); }
.apnav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.apnums { display: flex; gap: 4px; }
.apbtn { font-size: 12.5px; font-weight: 500; padding: 6px 11px; border-radius: 8px; border: 1px solid var(--line);
  background: #fff; color: var(--teal-deep); cursor: pointer; font-family: inherit; }
.apnum { min-width: 32px; padding: 6px 10px; border-radius: 8px; border: 1px solid var(--line); background: #fff;
  color: var(--teal-deep); font-weight: 500; font-size: 13px; cursor: pointer; font-family: inherit; }
.apnum.on { background: var(--teal); color: #fff; border-color: var(--teal); }
.apbtn[disabled] { opacity: .4; cursor: default; }
.admin-desktop-only, .admin-mobile-only { display: block; }
.admin-mobile-only { display: none; }
@media (max-width: 640px) {
  .admin-desktop-only { display: none; }
  .admin-mobile-only { display: block; }
}

/* ── "Mes badges" gallery (/profil/badges), ported from the prototype's .galhead/.famk/.sect-t/
   .badgegrid/.badge/.tile/.popup/.pbar — locked/unlocked is a CSS class, never an inline style. */
.badge-back { display: inline-block; margin: 0 0 16px; font-size: 13.5px; font-weight: 500; color: var(--teal-deep); text-decoration: none; }
.badge-back:hover { text-decoration: underline; }

.badge-ringrow { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.badge-ring { flex: 0 0 auto; width: 96px; height: 96px; }
.badge-ring-title { margin: 0; font-size: 19px; }

.badge-section-title { font-size: 16px; font-weight: 600; margin: 0 0 4px; }
.badge-family-title { font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--teal);
  font-weight: 600; margin: 18px 0 10px; }
.badge-section .badge-family-title:first-of-type { margin-top: 4px; }

.badge-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 12px; }
.badge-tile { border: 0; background: none; padding: 0; text-align: center; font-family: inherit; cursor: pointer; }
.badge-tile-ic { position: relative; aspect-ratio: 1; border-radius: 18px; display: grid; place-items: center;
  font-size: 26px; width: 100%; transition: transform .12s; }
.badge-tile:hover .badge-tile-ic { transform: translateY(-2px); }
.badge-tile.is-unlocked .badge-tile-ic { background: linear-gradient(150deg, #e9f4f1, #c7e4dd); box-shadow: var(--shadow-sm); }
.badge-tile.is-locked .badge-tile-ic { background: #edf1f0; filter: grayscale(1); opacity: .55; }
.badge-tile small { display: block; font-size: 10.5px; color: var(--muted); margin-top: 5px; }
.badge-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 18px; }
.badge-lock { position: absolute; right: -2px; bottom: -2px; font-size: 13px; background: #fff; border-radius: 50%;
  width: 20px; height: 20px; display: grid; place-items: center; box-shadow: var(--shadow-sm); }

/* Profil "Mes badges" teaser card (Task 5): same tile look as the gallery above, but the tiles are
   plain <div>s, not tap-to-explain buttons -- no pointer cursor / hover lift on them here. */
.badge-teaser-row { grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); }
.badge-teaser-row .badge-tile { cursor: default; }
.badge-teaser-row .badge-tile:hover .badge-tile-ic { transform: none; }

/* Tap-to-explain popup — reuses .tab-scrim/.tab-modal (already a bottom sheet on mobile), just its
   own content layout, centered like the prototype's .popup. */
.badge-modal { text-align: center; }
.badge-modal-icon { width: 78px; height: 78px; border-radius: 22px; display: grid; place-items: center;
  font-size: 36px; margin: 0 auto 14px; }
.badge-modal-icon.is-unlocked { background: linear-gradient(150deg, #e9f4f1, #c7e4dd); }
.badge-modal-icon.is-locked { background: #edf1f0; filter: grayscale(1); opacity: .6; }
.badge-modal h3 { margin: 0 0 6px; font-size: 18px; }
.badge-modal-req { color: var(--muted); font-size: 13.5px; margin: 0 0 6px; }
.badge-modal-progress { margin: 14px 0 0; }
.badge-bar { width: 100%; height: 8px; display: block; }
.badge-bar-bg { fill: #eef3f1; }
.badge-bar-fill { fill: var(--teal); }
.badge-modal-progress-text { font-size: 12px; color: var(--muted); margin: 6px 0 0; }
.badge-modal-unlocked { color: var(--teal-deep); font-weight: 600; font-size: 13.5px; margin: 0; }

@media (max-width: 720px) {
  .badge-ring-title { font-size: 17px; }
}

/* Unlock celebration — toast (assiduité families) + modal (rarer families), Badges Phase B Task 2.
   Built by public/js/badges.js via createElement/classList (never element.style); the toast fades
   its `.is-shown`/`.is-leaving` classes in/out, the modal reuses .tab-scrim/.tab-modal/.badge-modal
   (already a bottom sheet on mobile via the shared rules above). */
.badge-toast-host { position: fixed; top: 16px; right: 16px; z-index: 60; display: flex; flex-direction: column;
  gap: 10px; max-width: min(360px, calc(100vw - 32px)); pointer-events: none; }
.badge-toast { display: flex; align-items: flex-start; gap: 10px; background: #fff; border-radius: 16px;
  padding: 12px 14px; box-shadow: 0 10px 30px rgba(16,40,38,.2); opacity: 0; transform: translateY(-10px);
  transition: opacity .25s ease, transform .25s ease; pointer-events: auto; }
.badge-toast.is-shown { opacity: 1; transform: translateY(0); }
.badge-toast.is-leaving { opacity: 0; transform: translateY(-10px); }
.badge-toast-ic { font-size: 22px; line-height: 1; flex: 0 0 auto; }
.badge-toast-body { min-width: 0; }
.badge-toast-text { margin: 0 0 4px; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.badge-toast-link { font-size: 12.5px; color: var(--teal-deep); font-weight: 500; text-decoration: none; }
.badge-toast-link:hover, .badge-toast-link:focus-visible { text-decoration: underline; }

.badge-celebrate-modal .badge-celebrate-headline { margin: 0 0 4px; font-size: 19px; }
.badge-celebrate-modal .badge-celebrate-name { margin: 0 0 8px; font-weight: 600; color: var(--teal-deep); font-size: 14.5px; }
.badge-celebrate-modal .badge-celebrate-desc { margin: 0; }

/* Light CSS-only sparkle flourish on the celebration icon (spec: "no confetti lib, no inline style"). */
.badge-sparkle { position: relative; overflow: visible; }
.badge-sparkle::before, .badge-sparkle::after {
  content: '✨'; position: absolute; font-size: 15px; line-height: 1; opacity: 0;
  animation: badge-sparkle-pop 1.6s ease-in-out .15s infinite;
}
.badge-sparkle::before { top: -6px; left: -10px; }
.badge-sparkle::after  { bottom: -4px; right: -12px; animation-delay: .55s; }
@keyframes badge-sparkle-pop {
  0%   { opacity: 0; transform: scale(.4) rotate(-8deg); }
  35%  { opacity: 1; transform: scale(1) rotate(4deg); }
  70%  { opacity: 0; transform: scale(.7) rotate(0deg); }
  100% { opacity: 0; }
}

@media (max-width: 720px) {
  .badge-toast-host { left: 12px; right: 12px; top: 12px; max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  .badge-toast { transition: opacity .01ms; }
  .badge-sparkle::before, .badge-sparkle::after { animation: none; opacity: 0; }
}
