/* Frimas design tokens. Single source of truth.
   Changing a value here changes it on every page. */
:root {
  --nuit:#0E1F29;
  --ardoise:#1B3543;
  --acier:#62777F;
  --givre:#E3EAEC;
  --ligne:#C3D0D4;
  --ligne-2:#E9EEEF;
  --stable:#1E7F63;
  --attente:#B7861F;
  --ecart:#BF4A2E;
  --papier:#FFFFFF;
  --r-sm:2px;
  --r-md:4px;
  --r-lg:8px;
}

/* Type scale */
:root {
  --font-sans: Inter, ui-sans-serif, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-ar: "IBM Plex Sans Arabic", var(--font-sans);
}

/* Real brand mark (public/images/logo/frimas-primary-lockup-black.svg or
   -white.svg, picked per surface by background colour -- see each
   page's own comment next to where it is used). Sized in em so it
   scales with whatever font-size the surrounding .logo/.doc-brand rule
   already sets for that surface, rather than needing a pixel height
   tuned per page.

   Audit fix, 2026-09-11: first pass used 1.3em, which measured the
   image against the *whole* SVG viewBox (letters + the accent square +
   the underline bar beneath the wordmark) instead of against the
   letters alone -- next to the old plain-text logo it was noticeably
   oversized. The lettering itself only fills about 73% of that
   viewBox's height, so 1em here puts the letters at roughly the same
   cap-height the old CSS-drawn text logo had at the same font-size;
   the underline bar then sits just below that, which is the intended
   design (see pages/brand.html's own description of the lockup). */
.logo-img{height:1em;width:auto;display:block}

/* ---------- Route-grid background pattern ----------
   One faint texture, two colourways, shared by every surface that asks
   for it (the marketing hero, the booking wizard overlay, the signed-in
   app shell, the dashboard welcome band). A tileable SVG: a plain
   crosshair grid, a dashed diagonal crossing it like a route on a map,
   and a handful of waypoint dots at the joins -- logistics without
   reaching for a truck icon or a globe, and nothing brand.html's own
   "what this brand will not do" list already rules out (no gradient,
   no icon, no motion). Kept as inline SVG data URIs, so there is no
   extra request and no risk of it going missing on a host that does
   not serve /public the way this repo's own images do (see
   assets/js/vehicles.js). Opacity lives inside each SVG, not as a
   separate CSS opacity on the element, so it never dims the content
   sitting on top of it.
   --pattern-dark:  for the navy (--nuit) surfaces -- hero, welcome band.
   --pattern-light: for the pale (--givre) surfaces -- booking overlay,
                    the signed-in app shell background. */
:root {
  --pattern-dark: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='128'%20height='128'%3E%3Cg%20fill='none'%20stroke='%23E3EAEC'%20stroke-width='1'%20opacity='.05'%3E%3Cpath%20d='M0%2064H128M64%200V128'/%3E%3Cpath%20d='M0%200L128%20128M128%200L0%20128'%20stroke-dasharray='1%207'/%3E%3C/g%3E%3Cg%20fill='%231E7F63'%20opacity='.16'%3E%3Ccircle%20cx='64'%20cy='64'%20r='2'/%3E%3Ccircle%20cx='0'%20cy='0'%20r='1.4'/%3E%3Ccircle%20cx='128'%20cy='0'%20r='1.4'/%3E%3Ccircle%20cx='0'%20cy='128'%20r='1.4'/%3E%3Ccircle%20cx='128'%20cy='128'%20r='1.4'/%3E%3C/g%3E%3C/svg%3E");
  --pattern-light: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='128'%20height='128'%3E%3Cg%20fill='none'%20stroke='%23C3D0D4'%20stroke-width='1'%20opacity='.5'%3E%3Cpath%20d='M0%2064H128M64%200V128'/%3E%3Cpath%20d='M0%200L128%20128M128%200L0%20128'%20stroke-dasharray='1%207'/%3E%3C/g%3E%3Cg%20fill='%231E7F63'%20opacity='.14'%3E%3Ccircle%20cx='64'%20cy='64'%20r='2'/%3E%3Ccircle%20cx='0'%20cy='0'%20r='1.4'/%3E%3Ccircle%20cx='128'%20cy='0'%20r='1.4'/%3E%3Ccircle%20cx='0'%20cy='128'%20r='1.4'/%3E%3Ccircle%20cx='128'%20cy='128'%20r='1.4'/%3E%3C/g%3E%3C/svg%3E");
  --pattern-size: 128px;
}

html { -webkit-text-size-adjust: 100%; }
body { margin: 0; }
/* Found while testing the nav's log-in/dashboard swap: any element that also
   carries a class setting its own `display` (.btn included) silently wins
   over the plain [hidden] attribute, because author CSS beats the browser's
   default [hidden]{display:none}. Without this, "Log in" and "Dashboard"
   render at the same time regardless of session state. One rule, site-wide. */
[hidden] { display: none !important; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}


/* ---------- Motion ----------
   Purposeful, not decorative. The trace drawing is the brand idea, so it earns
   an entrance; everything added since is the same idea applied further: state
   feedback (hover, press, focus, arrival) plus a single, consistent way for
   content to enter as it's scrolled to. One system, reused everywhere, never
   simultaneous, never looping for its own sake.
   Anyone who has asked their system to reduce motion gets none of it. */

@keyframes frimas-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes frimas-fade{from{opacity:0}to{opacity:1}}
@keyframes frimas-draw{to{stroke-dashoffset:0}}
@keyframes frimas-van{from{opacity:0;transform:translate(650px,96px)}
                      to{opacity:1;transform:translate(700px,96px)}}
@keyframes frimas-blip{0%,100%{opacity:1}50%{opacity:.35}}
@keyframes frimas-pop{from{opacity:0;transform:scale(.7)}60%{transform:scale(1.06)}to{opacity:1;transform:none}}
@keyframes frimas-step-in{from{opacity:0;transform:translateX(8px)}to{opacity:1;transform:none}}

:root{--ease:cubic-bezier(.22,.61,.36,1)}

.trace-line{
  stroke-dasharray:2400;stroke-dashoffset:2400;
  animation:frimas-draw 2.4s .3s var(--ease) forwards;
}
.hero-van{
  opacity:0;
  animation:frimas-van 1s 1.1s var(--ease) forwards;
}

/* state feedback, shared */
.btn,.chip,.tile,.opt{transition:background 160ms var(--ease),border-color 160ms var(--ease),
  color 160ms var(--ease),transform 90ms var(--ease)}
.btn:active,.chip:active,.tile:active,.opt:active{transform:translateY(1px)}
.inp,input,select,textarea{transition:border-color 160ms var(--ease),
  background 160ms var(--ease),box-shadow 160ms var(--ease)}

/* ---------- Scroll reveal ----------
   assets/js/reveal.js adds .is-visible the first time an element with
   [data-reveal] crosses into the viewport, once, via IntersectionObserver.
   data-reveal="up" (default), "fade" or "scale" pick the starting state.
   A group of children (data-reveal-group) is staggered automatically —
   each child's transition-delay is set from its position, capped so a long
   grid doesn't keep the last card waiting. */
[data-reveal]{opacity:0;transition:opacity 560ms var(--ease),transform 560ms var(--ease);
  transition-delay:calc(var(--stg,0) * 65ms)}
[data-reveal="up"]{transform:translateY(22px)}
[data-reveal="fade"]{transform:none}
[data-reveal="scale"]{transform:scale(.96)}
[data-reveal].is-visible{opacity:1;transform:none}

/* ---------- Loading / busy state, shared ----------
   For a button mid real network work (sign-in, submit) — never a delay added
   just to show an animation. Text stays in the DOM (so layout doesn't jump);
   a spinner appears next to it via ::after. */
.btn.is-busy{position:relative;color:transparent!important;pointer-events:none}
.btn.is-busy::after{
  content:"";position:absolute;left:50%;top:50%;width:15px;height:15px;margin:-7.5px 0 0 -7.5px;
  border-radius:50%;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;
  animation:frimas-spin .7s linear infinite;
}
.btn.secondary.is-busy::after,.btn.sec.is-busy::after,.btn.quiet.is-busy::after{
  border-color:rgba(14,31,41,.2);border-top-color:var(--nuit);
}
@keyframes frimas-spin{to{transform:rotate(360deg)}}

/* skeleton loader, shared — for content waiting on a real fetch */
.skel{position:relative;overflow:hidden;background:var(--ligne-2);border-radius:var(--r-sm);color:transparent!important}
.skel::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.55),transparent);
  animation:frimas-shimmer 1.3s ease-in-out infinite}
@keyframes frimas-shimmer{from{transform:translateX(-100%)}to{transform:translateX(100%)}}

/* shake, shared — one bad field, not the whole form */
@keyframes frimas-shake{10%,90%{transform:translateX(-2px)}30%,70%{transform:translateX(3px)}50%{transform:translateX(-3px)}}
.shake-once{animation:frimas-shake .34s var(--ease)}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition-duration:.01ms!important}
  .trace-line{stroke-dashoffset:0}
  .hero-van{opacity:1;transform:translate(700px,96px)}
  [data-reveal]{opacity:1!important;transform:none!important}
  .skel::after{display:none}
}
