/* =========================================================================
 * system.css — Kreezalid portable storefront component layer
 * -------------------------------------------------------------------------
 * Theme- and framework-agnostic. Lives OUTSIDE the themes (served at
 * /assets/css/system.css) so any theme can adopt it. Every class is `kz-`
 * prefixed and every visual value is a `--kz-*` CSS custom property with a
 * neutral default — a theme opts in by overriding the tokens (load its own
 * stylesheet AFTER this one). Nothing here touches bare element selectors, so
 * it is safe to load alongside a legacy Bootstrap-3 theme.
 *
 * Companion runtime: /assets/js/kr-sdk.js (declarative `data-kz-action`).
 * ========================================================================= */

:root {
  /* Brand + accents (themes override) */
  --kz-color-primary:        #2563eb;
  --kz-color-primary-hover:  #1d4ed8;
  --kz-color-primary-tint:   #eff4ff;
  --kz-color-on-primary:     #ffffff;
  --kz-color-accent:         #f59e0b;
  --kz-color-secondary:      #0891b2;

  /* Ink + surfaces */
  --kz-color-ink:      #111827;   /* titles */
  --kz-color-body:     #374151;   /* body copy */
  --kz-color-subtle:   #6b7280;   /* secondary */
  --kz-color-muted:    #9ca3af;   /* muted */
  --kz-color-page:     #f8fafc;   /* page background */
  --kz-color-surface:  #ffffff;   /* cards */
  --kz-color-soft:     #f1f5f9;   /* soft fill */
  --kz-color-line:     #e5e7eb;   /* borders */

  /* States */
  --kz-color-success:      #0e9f6e;
  --kz-color-success-tint: #e9f9f0;
  --kz-color-danger:       #d92d20;
  --kz-color-danger-tint:  #fdecea;
  --kz-color-warning:      #b45309;
  --kz-color-warning-tint: #fef3c7;

  /* Badge defaults */
  --kz-badge-pro-bg:        #f59e0b;
  --kz-badge-pro-fg:        #111827;
  --kz-badge-individual-bg: #0891b2;
  --kz-badge-individual-fg: #ffffff;

  /* Shape */
  --kz-radius-sm: 8px;
  --kz-radius:    12px;
  --kz-radius-lg: 16px;
  --kz-radius-pill: 999px;

  /* Elevation */
  --kz-shadow:     0 2px 10px rgba(17, 24, 39, .08);
  --kz-shadow-lg:  0 10px 26px rgba(17, 24, 39, .16);
  --kz-ring:       0 0 0 3px var(--kz-color-primary-tint);

  /* Type */
  --kz-font:         ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --kz-font-display: var(--kz-font);

  /* Control sizing */
  --kz-control-h:    48px;
  --kz-field-h:      50px;

  /* Spacing scale */
  --kz-gap:   16px;
  --kz-gap-sm: 8px;
  --kz-gap-lg: 24px;

  /* Motion */
  --kz-transition: background-color .18s ease, color .18s ease, border-color .18s ease, box-shadow .25s ease, transform .2s ease;
}

/* Dark-mode token overrides. Toggled by the theme adding `.dark` to <html>. */
.dark {
  --kz-color-ink:     #e8ebf2;
  --kz-color-body:    #b6bdcd;
  --kz-color-subtle:  #98a1b6;
  --kz-color-muted:   #7e8699;
  --kz-color-page:    #10141f;
  --kz-color-surface: #1b2231;
  --kz-color-soft:    #242c3f;
  --kz-color-line:    #2a3247;
  /* Les teintes sémantiques DOIVENT être remappées ici : les valeurs claires
     (#fdecea, #fef3c7, #e9f9f0) sur une page sombre donnent des panneaux
     éblouissants — le défaut « conteneurs blancs en mode sombre » déjà corrigé
     ailleurs. Assombries, le texte posé sur --kz-color-ink reste lisible dans
     les deux thèmes sans règle supplémentaire. */
  --kz-color-danger-tint:  #3a1512;
  --kz-color-warning-tint: #3a2a0c;
  --kz-color-success-tint: #10301f;
  /* Les jours du calendrier qui ont des creneaux, en mode sombre. Litteraux et non derives
     d'un jeton : ce sont les valeurs donnees par le client. Un theme portable qui ne serait
     pas rose les redefinit ici plutot que d'aller reecrire les selecteurs du calendrier. */
  --kz-calendar-open-bg:   #fbe7f2;
  --kz-calendar-open-ink:  #e0237a;
  /* Et les teintes PLEINES avec elles. Le commentaire ci-dessus supposait que le
     texte reposait toujours sur --kz-color-ink ; ce n'est pas le cas de
     .kz-alert--*, .kz-badge--*, .kz-error ni des badges d'exception du calendrier,
     qui posent la couleur pleine SUR sa propre teinte. Assombrir la teinte sans
     éclaircir la couleur donnait du sombre sur sombre : mesuré en navigateur,
     2,76:1 pour warning, 3,35:1 pour danger, 4,24:1 pour success — les trois sous
     le minimum AA de 4,5:1. Éclaircies, elles passent respectivement à 8,3, 5,86
     et 7,46 sur leur teinte, et s'améliorent aussi sur --kz-color-surface (3,17
     -> 9,53 pour warning) où .kz-error et .kz-btn--danger les utilisent. */
  --kz-color-danger:  #f87171;
  --kz-color-warning: #fbbf24;
  --kz-color-success: #34d399;
}

/* -------------------------------------------------------------------------
 * Marquage LEGACY partagé (.alert / .notification)
 *
 * Ces classes ne viennent pas de la couche portable mais d'éléments PHP
 * communs à tous les tenants : bannière KYC Stripe (PSP.create_psp_account,
 * rendue AVANT l'en-tête), messages flash de Cake (Flash/default), sidebar
 * d'annonce, boutons de transaction, offres privées… Un thème portable
 * n'embarque ni Bootstrap 3 ni sa feuille `.alert`, si bien que tout ce
 * marquage s'affichait en texte nu, sans cadre ni couleur — la bannière
 * « Complétez vos informations Stripe » flottait en haut de page comme du
 * texte brut.
 *
 * Stylé ici plutôt que dans un thème : le marquage vient d'éléments partagés,
 * donc tout futur thème portable héritera du même problème.
 * ---------------------------------------------------------------------- */
.alert,
.notification {
  box-sizing: border-box;
  border: 1px solid var(--kz-color-line);
  border-radius: var(--kz-radius);
  background: var(--kz-color-soft);
  color: var(--kz-color-ink);
  padding: 14px 16px;
  font-size: 14.5px;
  line-height: 1.55;
}

.alert p,
.notification p { margin: 0 0 6px; }
.alert p:last-child,
.notification p:last-child { margin-bottom: 0; }

.alert a,
.notification a { color: inherit; font-weight: 600; text-decoration: underline; }

/* Le texte reste sur --kz-color-ink, qui bascule avec le thème ; seules la
   teinte de fond et la bordure portent la sémantique. */
.alert-danger,  .notification.error   { background: var(--kz-color-danger-tint);  border-color: var(--kz-color-danger); }
.alert-warning, .notification.warning { background: var(--kz-color-warning-tint); border-color: var(--kz-color-warning); }
.alert-success, .notification.success { background: var(--kz-color-success-tint); border-color: var(--kz-color-success); }
.alert-info                           { background: var(--kz-color-primary-tint); border-color: var(--kz-color-primary); }

/* La bannière KYC et les flash sont rendus hors de tout conteneur, directement
   sous <body> : sans largeur ni marges ils collaient aux bords de l'écran. */
.vendor_kyc_alert,
.notification { width: min(100% - 40px, 1200px); margin: 14px auto; }

/* Bannière KYC repliable. La ligne de titre reste toujours visible : replier n'est
   pas fermer, le vendeur doit pouvoir retrouver le message d'un clic. */
.kz-kyc__summary { display: flex; align-items: center; gap: 12px; cursor: pointer; list-style: none; }
.kz-kyc__summary::-webkit-details-marker { display: none; }
.kz-kyc__summary strong { flex: 1 1 auto; min-width: 0; }
/* Chevron dessiné en CSS : les thèmes portables ne chargent pas Font Awesome, et
   l'élément vit côté plateforme, donc pas de sprite SVG du thème non plus. */
.kz-kyc__toggle {
  flex: 0 0 auto; width: 9px; height: 9px; border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor; transform: rotate(45deg); margin-bottom: 4px;
  transition: var(--kz-transition); opacity: .7;
}
details.kz-kyc[open] .kz-kyc__toggle { transform: rotate(-135deg); margin-bottom: 0; }
.kz-kyc__body { margin-top: 8px; }
.kz-kyc__body p:last-child { margin-bottom: 0; }

/* Le flash utilise la grille Bootstrap 3 (.row + .col-*), absente ici : flex
   remet le message et sa croix côte à côte. */
.notification { display: flex; align-items: flex-start; gap: 12px; }
.notification > [class*="col-"] { padding: 0; width: auto; }
.notification .notification-msg { flex: 1 1 auto; min-width: 0; }
.notification .notification-end { flex: 0 0 auto; }
.notification .close { cursor: pointer; opacity: .65; }
.notification .close:hover { opacity: 1; }
/* .closed est ajoutée par le script jQuery du gabarit flash (fermeture manuelle
   et masquage automatique après 4 s). */
.notification.closed { display: none; }
/* Font Awesome n'est pas chargé par les thèmes portables (icônes en SVG inline),
   donc <i class="fa fa-times"> ne rendait rien : la croix était invisible. */
.notification .fa-times::before { content: "\2715"; font-style: normal; font-weight: 700; }

/* -------------------------------------------------------------------------
 * Layout
 * ---------------------------------------------------------------------- */
.kz-container { max-width: 1200px; margin-inline: auto; padding-inline: 20px; width: 100%; box-sizing: border-box; }
.kz-page { padding-top: 24px; padding-bottom: 56px; }

/* Generic "new section starts here" divider - a top border + breathing room, no bottom rule. */
.kz-divider-top { border-top: 1px solid var(--kz-color-line); padding-top: 14px; }

.kz-listing {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: var(--kz-gap-lg);
  align-items: start;
}
/* The gallery is its own grid child so that mobile can slot the price/CTA card
   between it and the sections; on desktop both simply stack in column 1. */
.kz-listing__gallery { grid-column: 1; }
.kz-listing__main { grid-column: 1; }
.kz-listing__aside { grid-column: 2; grid-row: 1 / span 2; position: sticky; top: 88px; }
@media (max-width: 900px) {
  .kz-listing { grid-template-columns: 1fr; }
  .kz-listing__gallery, .kz-listing__main, .kz-listing__aside { grid-column: 1; grid-row: auto; }
  /* See the vehicle, then the price and the actions, then the details. */
  .kz-listing__gallery { order: 1; }
  .kz-listing__aside { order: 2; position: static; }
  .kz-listing__main { order: 3; }
  .kz-listing__aside { position: static; }
}

/* Checkout summary: order recap + confirm form, side by side. */
.kz-checkout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--kz-gap-lg); align-items: start; }
@media (max-width: 860px) { .kz-checkout { grid-template-columns: 1fr; } }

.kz-stack { display: flex; flex-direction: column; gap: var(--kz-gap); }
.kz-section { margin-top: var(--kz-gap-lg); }
.kz-section__title {
  margin: 0 0 var(--kz-gap-sm);
  font-family: var(--kz-font-display);
  font-weight: 800;
  font-size: clamp(18px, 2.2vw, 22px);
  color: var(--kz-color-ink);
}

/* -------------------------------------------------------------------------
 * Card
 * ---------------------------------------------------------------------- */
.kz-card {
  background: var(--kz-color-surface);
  border-radius: var(--kz-radius-lg);
  box-shadow: var(--kz-shadow);
  box-sizing: border-box;
}
.kz-card--flat { box-shadow: none; border: 1px solid var(--kz-color-line); }
.kz-card--pad { padding: var(--kz-gap-lg); }

/* -------------------------------------------------------------------------
 * Buttons  (also used to re-skin legacy .btn via the element markup)
 * ---------------------------------------------------------------------- */
.kz-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  box-sizing: border-box; height: var(--kz-control-h); padding-inline: 22px;
  font-family: var(--kz-font-display); font-weight: 700; font-size: 14px; line-height: 1;
  border: 1.5px solid transparent; border-radius: var(--kz-radius-pill);
  cursor: pointer; text-decoration: none; user-select: none;
  transition: var(--kz-transition);
}
.kz-btn--primary { background: var(--kz-color-primary); color: var(--kz-color-on-primary); }
.kz-btn--primary:hover { background: var(--kz-color-primary-hover); color: var(--kz-color-on-primary); }
.kz-btn--secondary { background: var(--kz-color-ink); color: #fff; }
.kz-btn--secondary:hover { filter: brightness(1.15); color: #fff; }
.kz-btn--ghost { background: var(--kz-color-surface); color: var(--kz-color-ink); border-color: var(--kz-color-line); }
.kz-btn--ghost:hover { border-color: var(--kz-color-primary); color: var(--kz-color-primary); }
.kz-btn--block { width: 100%; }
/* For a label long enough to wrap. `.kz-btn` sets line-height 1 and a fixed
   height, which is right for one short line and cramped for two: the second
   line also fell back to the inherited left alignment. Height becomes a
   minimum so the extra leading has somewhere to go. */
.kz-btn--wrap { height: auto; min-height: var(--kz-control-h); padding-block: 11px; text-align: center; line-height: 1.35; }
.kz-btn--lg { height: 52px; font-size: 15px; }
.kz-btn[disabled], .kz-btn.is-disabled, .kz-btn.is-loading { opacity: .55; pointer-events: none; }
.kz-btn.is-loading { position: relative; color: transparent !important; }
.kz-btn.is-loading::after {
  content: ""; position: absolute; width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: kz-spin .6s linear infinite; color: var(--kz-color-on-primary);
}
@keyframes kz-spin { to { transform: rotate(360deg); } }

/* -------------------------------------------------------------------------
 * Price / badges / breadcrumb
 * ---------------------------------------------------------------------- */
.kz-price { font-family: var(--kz-font-display); font-weight: 800; font-size: 28px; color: var(--kz-color-primary); line-height: 1.1; }
.kz-price__unit { font-size: 14px; font-weight: 600; color: var(--kz-color-subtle); }
.kz-price__original { font-size: 16px; font-weight: 600; color: var(--kz-color-muted); text-decoration: line-through; margin-left: 8px; }

.kz-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--kz-font-display); font-weight: 700; font-size: 11px; letter-spacing: .04em;
  padding: 6px 10px; border-radius: var(--kz-radius-sm); text-transform: uppercase;
  background: var(--kz-color-soft); color: var(--kz-color-body);
}
.kz-badge--pro { background: var(--kz-badge-pro-bg); color: var(--kz-badge-pro-fg); }
.kz-badge--individual { background: var(--kz-badge-individual-bg); color: var(--kz-badge-individual-fg); }
.kz-badge--success { background: var(--kz-color-success-tint); color: var(--kz-color-success); }
.kz-badge--warning { background: var(--kz-color-warning-tint); color: var(--kz-color-warning); }
.kz-badge--danger  { background: var(--kz-color-danger-tint); color: var(--kz-color-danger); }

.kz-breadcrumb { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 13px; color: var(--kz-color-subtle); margin-bottom: var(--kz-gap); }
.kz-breadcrumb a { color: var(--kz-color-subtle); text-decoration: none; }
.kz-breadcrumb a:hover { color: var(--kz-color-primary); }
.kz-breadcrumb__sep { color: var(--kz-color-muted); }

/* -------------------------------------------------------------------------
 * Gallery
 * ---------------------------------------------------------------------- */
.kz-gallery { display: flex; flex-direction: column; gap: 10px; }
.kz-gallery__main {
  position: relative; aspect-ratio: 4 / 3; border-radius: var(--kz-radius-lg); overflow: hidden;
  background: var(--kz-color-soft);
}
.kz-gallery__main img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The swipeable variant. A MODIFIER rather than a change to .kz-gallery__main,
   so the single-photo fallback above keeps its current behaviour untouched.

   scroll-snap is what makes a drag land on a photo instead of between two.
   -webkit-overflow-scrolling gives older iOS the momentum it does not apply to
   snapping containers by default, and the scrollbar is hidden because on a
   touch screen the photos themselves say there are more. */
.kz-gallery__main--scroller {
  display: flex; overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory; scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.kz-gallery__main--scroller::-webkit-scrollbar { display: none; }
.kz-gallery__slide {
  flex: 0 0 100%; width: 100%; height: 100%;
  object-fit: cover; display: block;
  scroll-snap-align: center; scroll-snap-stop: always;
}

/* Someone who has asked their system for less motion means it for programmatic
   scrolling too: a thumbnail click should jump, not glide. Their own dragging
   is unaffected. */
@media (prefers-reduced-motion: reduce) {
  .kz-gallery__main--scroller { scroll-behavior: auto; }
}
.kz-gallery__thumbs { display: flex; gap: 8px; flex-wrap: wrap; }
.kz-gallery__thumb {
  width: 84px; height: 64px; border-radius: var(--kz-radius-sm); overflow: hidden; cursor: pointer;
  border: 2px solid transparent; background: var(--kz-color-soft); padding: 0; flex: 0 0 auto;
}
.kz-gallery__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.kz-gallery__thumb.is-active { border-color: var(--kz-color-primary); }
.kz-gallery__badge { position: absolute; top: 12px; left: 12px; }

/* A photo opens full screen on a click or a tap (.kz-lightbox below). The cursor
   says so on desktop; the button is the same door for the keyboard, since the
   photos themselves are not focusable. */
.kz-gallery__slide[data-lightbox-open] { cursor: zoom-in; }
/* The button sits on the stage, outside the track: a child of a scroll container
   would scroll away with the first photo. */
.kz-gallery__stage { position: relative; }
.kz-gallery__expand {
  position: absolute; right: 12px; bottom: 12px; z-index: 1;
  width: 40px; height: 40px; padding: 0; border: 0; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: rgba(17, 24, 39, .62); color: #fff;
}
.kz-gallery__expand:hover { background: rgba(17, 24, 39, .82); }
.kz-gallery__expand:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* -------------------------------------------------------------------------
 * Lightbox — the listing's photos, full screen
 *
 * The same scroll-snap track as the gallery, so a swipe moves between photos on
 * a phone with no library. Each photo is shown whole (contain, not cover): the
 * point of opening it is to see what the gallery crops.
 * ---------------------------------------------------------------------- */
.kz-lightbox {
  position: fixed; inset: 0; z-index: 1050;
  background: #0a0c10; color: #fff;
}
.kz-lightbox[hidden] { display: none; }
.kz-lightbox__track {
  display: flex; width: 100%; height: 100%;
  overflow-x: auto; overflow-y: hidden; overscroll-behavior: contain;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.kz-lightbox__track::-webkit-scrollbar { display: none; }
.kz-lightbox__slide {
  flex: 0 0 100%; width: 100%; height: 100%; margin: 0; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  padding: max(56px, env(safe-area-inset-top)) 12px max(56px, env(safe-area-inset-bottom));
  scroll-snap-align: center; scroll-snap-stop: always;
}
.kz-lightbox__slide img {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain; display: block; user-select: none;
}
.kz-lightbox__btn {
  position: absolute; z-index: 1; width: 48px; height: 48px; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .14); color: #fff;
}
.kz-lightbox__btn:hover { background: rgba(255, 255, 255, .26); }
.kz-lightbox__btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.kz-lightbox__btn[disabled] { opacity: .3; cursor: default; }
.kz-lightbox__close { top: max(10px, env(safe-area-inset-top)); right: 10px; }
.kz-lightbox__prev,
.kz-lightbox__next { top: 50%; transform: translateY(-50%); }
.kz-lightbox__prev { left: 12px; }
.kz-lightbox__next { right: 12px; }
.kz-lightbox__count {
  position: absolute; left: 50%; bottom: max(16px, env(safe-area-inset-bottom)); transform: translateX(-50%);
  margin: 0; padding: 4px 12px; border-radius: 999px;
  background: rgba(255, 255, 255, .14); font-size: 13px; font-variant-numeric: tabular-nums;
}
/* On a phone the swipe is the way through, and the arrows would sit on the photo. */
@media (hover: none) and (pointer: coarse) {
  .kz-lightbox__prev, .kz-lightbox__next { display: none; }
}

/* -------------------------------------------------------------------------
 * Spec / attribute list
 * ---------------------------------------------------------------------- */
.kz-specs { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 2px 20px; margin: 0; padding: 0; list-style: none; }
.kz-specs__row { display: flex; justify-content: space-between; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--kz-color-line); }
.kz-specs__label { color: var(--kz-color-subtle); font-size: 14px; }
.kz-specs__value { color: var(--kz-color-ink); font-weight: 600; font-size: 14px; text-align: right; }

/* -------------------------------------------------------------------------
 * Seller card
 * ---------------------------------------------------------------------- */
.kz-seller { display: flex; align-items: center; gap: 12px; }
.kz-seller__avatar { width: 46px; height: 46px; border-radius: 50%; object-fit: cover; background: var(--kz-color-soft); flex: 0 0 auto; }
.kz-seller__name { font-family: var(--kz-font-display); font-weight: 700; font-size: 15px; color: var(--kz-color-ink); text-decoration: none; }
.kz-seller__name:hover { color: var(--kz-color-primary); }
.kz-seller__meta { font-size: 13px; color: var(--kz-color-subtle); }

/* -------------------------------------------------------------------------
 * Prose (rich text description)
 * ---------------------------------------------------------------------- */
.kz-prose { color: var(--kz-color-body); font-size: 15px; line-height: 1.65; }
.kz-prose h1, .kz-prose h2, .kz-prose h3 { color: var(--kz-color-ink); font-family: var(--kz-font-display); }
.kz-prose a { color: var(--kz-color-primary); }
.kz-prose img { max-width: 100%; height: auto; border-radius: var(--kz-radius-sm); }
.kz-prose ul, .kz-prose ol { padding-left: 1.25em; }

/* Mode sombre : neutraliser les couleurs INLINE du contenu.
   .kz-prose est bien adaptatif (var(--kz-color-body)), mais le corps des pages
   CMS vient de l'éditeur du back-office, qui écrit ses couleurs en dur dans le
   HTML : sur les mentions légales, un conteneur `style="color: #333"` plus des
   titres en #34495e et des span en #993366. Une couleur inline l'emporte sur
   toute règle de feuille de style, quelle que soit sa spécificité — d'où le
   !important, seul moyen de reprendre la main. Résultat mesuré avant
   correction : 1,46:1 sur le fond sombre, soit un texte illisible.

   Le mode clair n'est pas touché : ces couleurs y sont lisibles et relèvent du
   choix éditorial du client. En sombre, une couleur pensée pour un fond blanc
   est de toute façon fausse ; on la ramène donc au jeu de tokens du thème
   plutôt que de tenter de la préserver. */
.dark .kz-prose [style*="color"] { color: var(--kz-color-body) !important; }
/* Les titres et les liens gardent leur rôle : la règle ci-dessus les aurait
   sinon aplatis sur la couleur de corps. Le second sélecteur vise les span
   colorés PLACÉS DANS un titre, cas présent sur les pages légales. */
.dark .kz-prose :is(h1, h2, h3, h4, h5, h6)[style*="color"],
.dark .kz-prose :is(h1, h2, h3, h4, h5, h6) [style*="color"] { color: var(--kz-color-ink) !important; }
/* Les liens de contenu prennent une version ÉCLAIRCIE du rose de marque en
   sombre : mesuré sur la carte (#1b2231), --kz-color-primary ne donne que
   3,37:1, sous le seuil AA de 4,5:1 pour du texte. #ff5fae monte à 5,68:1 sur
   la carte et 6,57:1 sur la page, en restant reconnaissable comme la couleur
   de marque. Le jeton global n'est pas touché : boutons et aplats gardent le
   rose exact, où le contraste se joue sur le fond et non sur le texte. */
.dark .kz-prose a { color: #ff5fae; }
.dark .kz-prose a[style*="color"],
.dark .kz-prose a [style*="color"] { color: #ff5fae !important; }

/* -------------------------------------------------------------------------
 * Fields
 * ---------------------------------------------------------------------- */
.kz-label { display: block; font-size: 13px; font-weight: 700; color: var(--kz-color-body); margin-bottom: 6px; }
.kz-field {
  width: 100%; box-sizing: border-box; height: var(--kz-field-h); padding-inline: 16px;
  font: 15px var(--kz-font); color: var(--kz-color-ink); background: var(--kz-color-surface);
  border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius); outline: none;
  transition: var(--kz-transition);
}
textarea.kz-field { height: auto; padding: 12px 16px; line-height: 1.5; resize: vertical; }
.kz-field:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }
.kz-help  { font-size: 12.5px; color: var(--kz-color-muted); margin-top: 6px; }
.kz-error { font-size: 12.5px; font-weight: 600; color: var(--kz-color-danger); margin-top: 6px; }

/* -------------------------------------------------------------------------
 * Alerts
 * ---------------------------------------------------------------------- */
.kz-alert { border-radius: var(--kz-radius); padding: 12px 16px; font-size: 14px; line-height: 1.5; }
.kz-alert--warning { background: var(--kz-color-warning-tint); color: var(--kz-color-warning); }
.kz-alert--success { background: var(--kz-color-success-tint); color: var(--kz-color-success); }
.kz-alert--danger  { background: var(--kz-color-danger-tint); color: var(--kz-color-danger); }

/* -------------------------------------------------------------------------
 * Modal (driven by Kr.modal)
 * ---------------------------------------------------------------------- */
.kz-modal { position: fixed; inset: 0; z-index: 1000; display: none; }
.kz-modal.is-open { display: block; }
.kz-modal__overlay { position: absolute; inset: 0; background: rgba(17, 24, 39, .55); animation: kz-fade .2s ease; }
.kz-modal__dialog {
  position: relative; z-index: 1; max-width: 520px; width: calc(100% - 40px);
  margin: 8vh auto; background: var(--kz-color-surface); color: var(--kz-color-ink);
  border-radius: var(--kz-radius-lg); box-shadow: var(--kz-shadow-lg); padding: 24px;
  animation: kz-pop .25s ease; max-height: 84vh; overflow: auto; box-sizing: border-box;
}
.kz-modal__close {
  position: absolute; top: 12px; right: 12px; width: 36px; height: 36px; border: 0; cursor: pointer;
  background: var(--kz-color-soft); border-radius: 50%; color: var(--kz-color-subtle); font-size: 18px; line-height: 1;
}
.kz-modal__close:hover { color: var(--kz-color-ink); }

/* Server-rendered order action fragments (accept/decline/ship/…) are Bootstrap
   modal markup. The portable themes ship no Bootstrap, so map that markup onto
   the design system — scoped to .kz-order-modal, never leaking outside it. */
.kz-order-modal .modal-dialog, .kz-order-modal .modal-content { display: contents; }
.kz-order-modal .modal-header { padding: 0 34px 0 0; margin-bottom: 14px; border: 0; }
.kz-order-modal .modal-header .close { display: none; } /* the dialog draws its own */
.kz-order-modal .modal-title { margin: 0; font-family: var(--kz-font-display); font-weight: 800; font-size: 19px; color: var(--kz-color-ink); }
.kz-order-modal .modal-body { padding: 0; color: var(--kz-color-body); font-size: 14.5px; line-height: 1.55; }
.kz-order-modal .modal-body p { margin: 0 0 10px; }
.kz-order-modal .modal-body p:last-child { margin-bottom: 0; }
.kz-order-modal .modal-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 10px; padding: 0; margin-top: 22px; border: 0; }
.kz-order-modal .btn, .kz-order-modal input[type="submit"], .kz-order-modal button[type="submit"] {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  box-sizing: border-box; height: var(--kz-control-h); padding-inline: 22px;
  font-family: var(--kz-font-display); font-weight: 700; font-size: 14px; line-height: 1;
  border: 1.5px solid transparent; border-radius: var(--kz-radius-pill);
  background: var(--kz-color-primary); color: var(--kz-color-on-primary);
  cursor: pointer; text-decoration: none; transition: var(--kz-transition);
}
.kz-order-modal .btn:hover, .kz-order-modal input[type="submit"]:hover, .kz-order-modal button[type="submit"]:hover { background: var(--kz-color-primary-hover); color: var(--kz-color-on-primary); }
.kz-order-modal .btn-secondary, .kz-order-modal .btn.decline {
  background: var(--kz-color-surface); color: var(--kz-color-ink); border-color: var(--kz-color-line);
}
.kz-order-modal .btn-secondary:hover, .kz-order-modal .btn.decline:hover { background: var(--kz-color-soft); color: var(--kz-color-ink); }
.kz-order-modal [disabled], .kz-order-modal .is-loading { opacity: .55; pointer-events: none; }
/* Textareas/selects the decline & ship fragments carry (refusal reason, tracking…). */
.kz-order-modal input[type="text"], .kz-order-modal input[type="number"], .kz-order-modal select, .kz-order-modal textarea {
  width: 100%; box-sizing: border-box; padding: 11px 14px; font: 14.5px var(--kz-font);
  color: var(--kz-color-ink); background: var(--kz-color-surface);
  border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius); outline: none;
}
.kz-order-modal input:focus, .kz-order-modal select:focus, .kz-order-modal textarea:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }
.kz-order-modal label { display: block; margin: 12px 0 6px; font-weight: 600; font-size: 13.5px; color: var(--kz-color-ink); }
@keyframes kz-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes kz-pop { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }

/* -------------------------------------------------------------------------
 * Toast (driven by Kr.toast)
 * ---------------------------------------------------------------------- */
.kz-toasts { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 1100; display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; }
.kz-toast {
  display: flex; align-items: center; gap: 8px; background: var(--kz-color-ink); color: #fff;
  font-size: 13px; font-weight: 600; border-radius: var(--kz-radius-pill); padding: 11px 20px;
  box-shadow: var(--kz-shadow-lg); animation: kz-pop .25s ease; white-space: nowrap;
}
.kz-toast--success { --dot: var(--kz-color-success); }
.kz-toast--error   { --dot: var(--kz-color-danger); }
.kz-toast::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--dot, var(--kz-color-primary)); }

/* -------------------------------------------------------------------------
 * Cart
 * ---------------------------------------------------------------------- */
.kz-cart { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: var(--kz-gap-lg); align-items: start; }
.kz-cart__aside { position: sticky; top: 88px; }
@media (max-width: 900px) { .kz-cart { grid-template-columns: 1fr; } .kz-cart__aside { position: static; } }

.kz-cart-item { display: flex; gap: 16px; align-items: flex-start; padding: 16px 0; border-bottom: 1px solid var(--kz-color-line); }
.kz-cart-item__img { width: 96px; height: 96px; border-radius: var(--kz-radius); overflow: hidden; background: var(--kz-color-soft); flex: 0 0 auto; }
.kz-cart-item__img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.kz-cart-item__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.kz-cart-item__title { display: block; margin: 0; font-family: var(--kz-font-display); font-weight: 700; font-size: 15.5px; color: var(--kz-color-ink); text-decoration: none; }
.kz-cart-item__title:hover { color: var(--kz-color-primary); }
.kz-cart-item__meta { font-size: 13px; color: var(--kz-color-subtle); }
.kz-cart-item__price { font-family: var(--kz-font-display); font-weight: 800; font-size: 16px; color: var(--kz-color-ink); white-space: nowrap; }
.kz-cart-item__remove { font-size: 13px; color: var(--kz-color-subtle); text-decoration: none; }
.kz-cart-item__remove:hover { color: var(--kz-color-danger); text-decoration: underline; }

.kz-summary-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 8px 0; font-size: 14px; color: var(--kz-color-body); }
.kz-summary-row--total { border-top: 1px solid var(--kz-color-line); margin-top: 6px; padding-top: 14px; font-family: var(--kz-font-display); font-weight: 800; font-size: 18px; color: var(--kz-color-ink); }
.kz-summary-row__value { font-weight: 700; color: var(--kz-color-ink); }
.kz-summary-row--discount .kz-summary-row__value { color: var(--kz-color-success); }

/* -------------------------------------------------------------------------
 * Checkout summary date range (see .kz-checkout above for the page grid)
 * ---------------------------------------------------------------------- */
.kz-checkout-dates { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.kz-checkout-dates__label { font-size: 12px; color: var(--kz-color-subtle); }
.kz-checkout-dates__value { font-weight: 700; color: var(--kz-color-ink); }
.kz-checkout-dates__arrow { color: var(--kz-color-muted); }

/* -------------------------------------------------------------------------
 * Page hero (simple page headers: blog, contact…)
 * ---------------------------------------------------------------------- */
.kz-hero { text-align: center; padding: clamp(32px, 5vw, 56px) 0 clamp(20px, 3vw, 32px); }
.kz-hero__title { margin: 0; font-family: var(--kz-font-display); font-weight: 800; font-size: clamp(26px, 4vw, 40px); color: var(--kz-color-ink); }
.kz-hero__subtitle { margin: 10px auto 0; max-width: 620px; color: var(--kz-color-subtle); font-size: clamp(15px, 1.6vw, 17px); line-height: 1.55; }

/* -------------------------------------------------------------------------
 * Blog
 * ---------------------------------------------------------------------- */
.kz-blog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--kz-gap-lg); }
.kz-article-card { display: flex; flex-direction: column; overflow: hidden; text-decoration: none; color: var(--kz-color-ink); background: var(--kz-color-surface); border-radius: var(--kz-radius-lg); box-shadow: var(--kz-shadow); transition: var(--kz-transition); }
.kz-article-card:hover { box-shadow: var(--kz-shadow-lg); color: var(--kz-color-ink); }
.kz-article-card__img { aspect-ratio: 16 / 9; background: var(--kz-color-soft) center/cover no-repeat; }
.kz-article-card__body { padding: 16px 18px 20px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.kz-article-card__cat { font-family: var(--kz-font-display); font-weight: 700; font-size: 12px; color: var(--kz-color-primary); text-transform: uppercase; letter-spacing: .04em; }
.kz-article-card__title { font-family: var(--kz-font-display); font-weight: 700; font-size: 17px; line-height: 1.3; margin: 0; }
.kz-article-card__summary { font-size: 14px; color: var(--kz-color-subtle); line-height: 1.55; flex: 1; }
.kz-article-card__date { font-size: 12.5px; color: var(--kz-color-muted); }

.kz-article { max-width: 760px; margin: 0 auto; }
.kz-article__hero { width: 100%; aspect-ratio: 16 / 8; object-fit: cover; border-radius: var(--kz-radius-lg); background: var(--kz-color-soft); margin-bottom: 20px; }
.kz-article__meta { display: flex; gap: 10px; align-items: center; font-size: 13px; color: var(--kz-color-subtle); margin-bottom: 10px; }
.kz-article__title { font-family: var(--kz-font-display); font-weight: 800; font-size: clamp(24px, 3.4vw, 36px); color: var(--kz-color-ink); line-height: 1.2; margin: 0 0 20px; }

/* Wraps the Bootstrap-markup pagination element (ul.pagination > li.page-item > a.page-link). */
.kz-pagination { margin-top: 32px; }
.kz-pagination .pagination, .kz-pagination ul { display: flex; gap: 6px; justify-content: center; align-items: center; flex-wrap: wrap; list-style: none; margin: 0; padding: 0; }
.kz-pagination .page-item { display: inline-flex; }
.kz-pagination .page-link, .kz-pagination a, .kz-pagination span { display: inline-flex; min-width: 38px; height: 38px; padding: 0 10px; align-items: center; justify-content: center; border-radius: var(--kz-radius-sm); border: 1px solid var(--kz-color-line); color: var(--kz-color-body); text-decoration: none; font-size: 14px; font-weight: 600; box-sizing: border-box; }
.kz-pagination .page-link:hover, .kz-pagination a:hover { border-color: var(--kz-color-primary); color: var(--kz-color-primary); }
.kz-pagination .page-item.active .page-link, .kz-pagination .active .page-link, .kz-pagination .current { background: var(--kz-color-primary); border-color: var(--kz-color-primary); color: var(--kz-color-on-primary); }
.kz-pagination .page-item.disabled .page-link { opacity: .4; pointer-events: none; }
.kz-pagination .ellipsis .page-link { border: 0; }
/* prev/next wrap their arrow in a nested <span> — don't turn that into a second box. */
.kz-pagination .page-link span, .kz-pagination a span { border: 0; padding: 0; min-width: 0; height: auto; background: none; box-shadow: none; display: inline; }

/* -------------------------------------------------------------------------
 * Tabs (e.g. account-area sub-navigation: "reviews about you" / "by you")
 * ---------------------------------------------------------------------- */
.kz-tabs { display: flex; gap: 20px; border-bottom: 1px solid var(--kz-color-line); }
.kz-tab {
  display: inline-flex; align-items: center; padding: 10px 2px; margin-bottom: -1px;
  font-family: var(--kz-font-display); font-weight: 700; font-size: 14px;
  color: var(--kz-color-subtle); text-decoration: none; border-bottom: 2px solid transparent;
}
.kz-tab:hover { color: var(--kz-color-ink); }
.kz-tab.is-active { color: var(--kz-color-primary); border-bottom-color: var(--kz-color-primary); }

/* -------------------------------------------------------------------------
 * Star rating / unread indicator
 * ---------------------------------------------------------------------- */
.kz-stars { color: var(--kz-color-accent); font-size: 15px; letter-spacing: 1px; }
.kz-unread-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--kz-color-danger); vertical-align: middle; margin-left: 4px; }

/* Rating INPUT: five radios drawn as stars, no JavaScript.
   The stars are emitted 5..1 and laid out row-reverse, so the plain sibling
   combinator can light the chosen star and everything visually before it - which
   is why the DOM order is reversed rather than the display being hacked.
   Radios (not a number field, not a script widget) also give the group native
   `required` validation and arrow-key navigation for free. */
.kz-rating { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; gap: 4px; }
.kz-rating input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.kz-rating label { cursor: pointer; font-size: 30px; line-height: 1; color: var(--kz-color-line); transition: var(--kz-transition); }
.kz-rating input:checked ~ label,
.kz-rating label:hover,
.kz-rating label:hover ~ label { color: var(--kz-color-accent); }
.kz-rating input:focus-visible + label { outline: 2px solid var(--kz-color-primary); outline-offset: 2px; border-radius: 4px; }

/* -------------------------------------------------------------------------
 * Search / category layout + filters sidebar
 * ---------------------------------------------------------------------- */
.kz-search { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: var(--kz-gap-lg); align-items: start; }
.kz-search__aside { position: sticky; top: 88px; }
@media (max-width: 860px) { .kz-search { grid-template-columns: 1fr; } .kz-search__aside { position: static; } }

/* Style the server-rendered filter/facet + sort markup (kindshare classes). */
.kz-filters .filter_title { font-family: var(--kz-font-display); font-weight: 700; font-size: 14px; color: var(--kz-color-ink); margin: 16px 0 8px; }
.kz-filters .filter_title:first-child { margin-top: 0; }
.kz-filters ul { list-style: none; margin: 0 0 8px; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.kz-filters li { line-height: 1.4; }
/* Facet links only. Without the :not(), this also captured any .kz-btn dropped
   into a filter card and flattened it to a block with the facet's own padding
   and radius, killing the button's centring. */
.kz-filters a:not(.kz-btn) { display: block; padding: 6px 10px; border-radius: var(--kz-radius-sm); color: var(--kz-color-body); text-decoration: none; font-size: 14px; }
.kz-filters a:not(.kz-btn):hover { background: var(--kz-color-soft); color: var(--kz-color-ink); }
.kz-filters a.active:not(.kz-btn) { background: var(--kz-color-primary-tint); color: var(--kz-color-primary); font-weight: 700; }
.kz-filters .result_subcategory_list { margin-left: 10px; border-left: 1px solid var(--kz-color-line); padding-left: 6px; }
.kz-filters input[type="text"], .kz-filters input[type="number"], .kz-filters input[type="search"],
.kz-filters input[type="date"], .kz-filters select {
  width: 100%; box-sizing: border-box; height: 42px; padding: 0 12px; font: 14px var(--kz-font);
  color: var(--kz-color-ink); background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line);
  border-radius: var(--kz-radius); outline: none; margin-bottom: 10px;
}
.kz-filters input:focus, .kz-filters select:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }
.kz-filters label { display: flex; gap: 8px; align-items: center; font-size: 14px; color: var(--kz-color-body); padding: 4px 0; cursor: pointer; }
.kz-filters input[type="checkbox"], .kz-filters input[type="radio"] { accent-color: var(--kz-color-primary); width: auto; height: auto; margin: 0; }

/* Accordion facets: folded by default, unfolded when they carry an active filter.
   The <details> marker is replaced by a chevron so the head reads as a control. */
.kz-facet { border-bottom: 1px solid var(--kz-color-line); }
.kz-facet:last-of-type { border-bottom: 0; }
.kz-facet__head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 12px 0; cursor: pointer; list-style: none; user-select: none;
}
.kz-facet__head::-webkit-details-marker { display: none; }
.kz-facet__head::after {
  content: ""; width: 8px; height: 8px; flex: 0 0 auto; margin-right: 2px;
  border-right: 2px solid var(--kz-color-subtle); border-bottom: 2px solid var(--kz-color-subtle);
  transform: rotate(45deg) translateY(-2px); transition: transform .15s ease;
}
.kz-facet[open] > .kz-facet__head::after { transform: rotate(-135deg) translateY(-2px); }
.kz-facet__head:hover::after { border-color: var(--kz-color-ink); }
.kz-facet__head:focus-visible { outline: 2px solid var(--kz-color-primary); outline-offset: 2px; }
.kz-facet__head .filter_title { margin: 0; }
.kz-facet__body { padding: 0 0 12px; }

/* Offer tabs: one kind of offer at a time, never mixed in a single result list. */
.kz-tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 16px; border-bottom: 1.5px solid var(--kz-color-line); }
.kz-tab {
  display: inline-flex; align-items: center; gap: 7px; padding: 10px 16px;
  font-family: var(--kz-font-display); font-weight: 600; font-size: 15px;
  color: var(--kz-color-subtle); text-decoration: none;
  border-bottom: 2.5px solid transparent; margin-bottom: -1.5px;
}
.kz-tab:hover { color: var(--kz-color-ink); }
.kz-tab.is-active { color: var(--kz-color-primary); border-bottom-color: var(--kz-color-primary); }
.kz-tab__count {
  font-family: var(--kz-font); font-weight: 600; font-size: 12px; line-height: 1;
  padding: 3px 7px; border-radius: 999px;
  color: var(--kz-color-subtle); background: var(--kz-color-soft);
}
.kz-tab.is-active .kz-tab__count { color: var(--kz-color-primary); background: var(--kz-color-primary-tint); }
.kz-tab:focus-visible { outline: 2px solid var(--kz-color-primary); outline-offset: -2px; }

/* Period picker. The grid itself is the core calendar widget — same one the listing
   page books with — so only its shell is dressed here. */
.kz-daterange { position: relative; }
.kz-daterange--enhanced .kz-dates { display: none; }
.kz-daterange__summary {
  width: 100%; display: block; text-align: left;
  padding: 0 12px; height: 42px; margin: 0;
  font: 500 14px var(--kz-font); color: var(--kz-color-muted);
  background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line);
  border-radius: var(--kz-radius); cursor: pointer;
}
.kz-daterange__summary.is-set { color: var(--kz-color-ink); font-weight: 600; }
.kz-daterange__summary:hover { border-color: var(--kz-color-primary); }
.kz-daterange__summary:focus-visible { outline: 2px solid var(--kz-color-primary); outline-offset: 2px; }
.kz-daterange__grid { display: none; margin-top: 8px; }
.kz-daterange.is-open .kz-daterange__grid { display: block; }

/* Availability: labels on one row, the two date fields side by side on the next, so the
   block stays two rows tall in a narrow sidebar instead of stacking into four. The grid
   is what keeps each field under its own label without nesting them. */
.kz-dates { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 8px; align-items: end; }
.kz-dates__label { margin: 0; font-size: 12.5px; color: var(--kz-color-subtle); }
.kz-dates input[type="date"] { margin: 0; min-width: 0; padding: 0 8px; }

/* City search: the suggestion list hangs under its input, hence the positioned parent. */
.kz-city-field { position: relative; }
.kz-city-suggestions {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% - 6px);
  margin: 0; padding: 4px; list-style: none;
  background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line);
  border-radius: var(--kz-radius); box-shadow: 0 6px 20px rgba(26,32,44,.12);
}
.kz-city-suggestions:empty { display: none; }
.kz-city-suggestion {
  padding: 7px 10px; border-radius: calc(var(--kz-radius) - 4px);
  font-size: 14px; color: var(--kz-color-body); cursor: pointer;
}
.kz-city-suggestion:hover { background: var(--kz-color-soft); color: var(--kz-color-ink); }

.kz-radius { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--kz-color-subtle); }
.kz-radius span { flex: 0 0 auto; }
.kz-radius select { margin: 0; }

/* Yes / no / either. Three states, because a checkbox can only ever say "yes". */
.kz-tristate { display: flex; gap: 4px; padding: 2px; background: var(--kz-color-soft); border-radius: var(--kz-radius); }
.kz-tristate__opt {
  flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: 5px;
  padding: 7px 6px; margin: 0; border-radius: calc(var(--kz-radius) - 2px);
  font-size: 13px; font-weight: 600; color: var(--kz-color-subtle); cursor: pointer;
  white-space: nowrap;
}
.kz-tristate__opt:hover { color: var(--kz-color-ink); }
.kz-tristate__opt.is-active { color: var(--kz-color-primary); background: var(--kz-color-surface); box-shadow: 0 1px 3px rgba(26,32,44,.1); }
.kz-tristate__opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.kz-tristate__opt:focus-within { outline: 2px solid var(--kz-color-primary); outline-offset: 1px; }
.kz-tristate__count { font-weight: 500; font-size: 11.5px; color: var(--kz-color-muted); }

/* Active filters, above the results: what is narrowing the list, one removal each. */
.kz-active-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.kz-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 6px 5px 10px;
  font-size: 13px; line-height: 1.3; color: var(--kz-color-ink);
  background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line);
  border-radius: 999px;
}
.kz-chip__group { color: var(--kz-color-muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em; }
.kz-chip__label { font-weight: 600; }
.kz-chip__remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; color: var(--kz-color-subtle);
  background: var(--kz-color-soft); text-decoration: none;
}
.kz-chip__remove:hover { color: var(--kz-color-surface); background: var(--kz-color-ink); }
.kz-chip__remove:focus-visible { outline: 2px solid var(--kz-color-primary); outline-offset: 2px; }
.kz-chip--reset { padding: 5px 12px; color: var(--kz-color-subtle); text-decoration: none; font-weight: 600; }
.kz-chip--reset:hover { color: var(--kz-color-ink); border-color: var(--kz-color-line-strong, var(--kz-color-line)); }

.kz-sort { display: flex; align-items: center; gap: 8px; }
.kz-sort .sort-by select, .kz-sort select { height: 42px; padding: 0 34px 0 12px; font: 14px var(--kz-font); color: var(--kz-color-ink); background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius); outline: none; cursor: pointer; }

/* -------------------------------------------------------------------------
 * Embedded Cake forms (contact, login…): give the server-rendered <form> the
 * portable look without rebuilding it. Wrap the injected form in .kz-embed-form.
 * ---------------------------------------------------------------------- */
.kz-embed-form input[type="text"], .kz-embed-form input[type="email"],
.kz-embed-form input[type="tel"], .kz-embed-form input[type="password"],
.kz-embed-form input[type="search"], .kz-embed-form input[type="url"],
.kz-embed-form input[type="date"], .kz-embed-form input[type="datetime-local"],
.kz-embed-form input[type="month"], .kz-embed-form input[type="time"],
.kz-embed-form input[type="number"],
.kz-embed-form select, .kz-embed-form textarea {
  width: 100%; box-sizing: border-box; min-height: var(--kz-field-h); padding: 12px 16px;
  font: 15px var(--kz-font); color: var(--kz-color-ink); background: var(--kz-color-surface);
  border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius); outline: none; margin-bottom: 14px;
}
.kz-embed-form textarea { min-height: 120px; line-height: 1.5; resize: vertical; }
/* Native date/time controls: normalise height + tidy the picker indicator. */
.kz-embed-form input[type="date"], .kz-embed-form input[type="datetime-local"],
.kz-embed-form input[type="month"], .kz-embed-form input[type="time"] { appearance: none; -webkit-appearance: none; }
.kz-embed-form input[type="date"]::-webkit-calendar-picker-indicator,
.kz-embed-form input[type="datetime-local"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .6; }
.dark .kz-embed-form input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1); }

/* Phone attribute widget (self-contained markup) — give it a clean, theme-independent
   layout using the portable tokens: country selector + number side by side, aligned
   to the same field height. */
.kz-embed-form .phone-attribute-wrapper { display: flex; gap: 10px; align-items: stretch; margin-bottom: 14px; }
.kz-embed-form .phone-country-wrapper { flex: 0 0 auto; width: 128px; }
.kz-embed-form .phone-number-wrapper { flex: 1 1 auto; min-width: 0; }
.kz-embed-form .phone-attribute-wrapper .phone-country-select,
.kz-embed-form .phone-attribute-wrapper .phone-number-input { width: 100%; margin-bottom: 0; height: var(--kz-field-h); }
.kz-embed-form input:focus, .kz-embed-form select:focus, .kz-embed-form textarea:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }
.kz-embed-form label { display: block; font-size: 13px; font-weight: 700; color: var(--kz-color-body); margin-bottom: 6px; }
.kz-embed-form .checkbox label, .kz-embed-form label.checkbox { display: flex; gap: 8px; align-items: flex-start; font-weight: 500; }
.kz-embed-form input[type="checkbox"], .kz-embed-form input[type="radio"] { width: auto; min-height: 0; margin: 3px 0 0; accent-color: var(--kz-color-primary); }
.kz-embed-form button, .kz-embed-form input[type="submit"], .kz-embed-form .btn {
  width: 100%; height: 52px; border: 0; cursor: pointer; color: var(--kz-color-on-primary);
  background: var(--kz-color-primary); border-radius: var(--kz-radius-pill);
  font: 700 15px var(--kz-font-display); margin-top: 4px; transition: var(--kz-transition);
}
.kz-embed-form button:hover, .kz-embed-form input[type="submit"]:hover { background: var(--kz-color-primary-hover); }

/* Image uploader (Kreezalid/Listing.uploader), as embedded by the AdvancedStore gallery.
   Two things that stylesheet leaves to Bootstrap 3, which a portable theme does not ship:

   1. Stacking. It paints an uploaded thumbnail at z-index 999 and its delete button at
      9999, and nothing between them and the root creates a stacking context - so those
      numbers compete with the page chrome and win. On mobile the thumbnails were drawn
      ON TOP of the fixed bottom bar. `isolation` confines them to the gallery without
      changing their order inside it.
   2. Float containment. The thumbnails are floated and BS3's `.row` clearfix used to
      close them; without it the <aside> collapses to zero height. Laying them out as a
      wrapping flex row removes the float entirely. The row gap is wider than the column
      one because the delete button sits 22px ABOVE its tile and needs that room.

   The tile is `min(170px, calc(50% - 10px))` wide: 170px is its natural size, but it may
   never take more than half a row, so there are ALWAYS at least two per row. At a fixed
   170px it took 360px of gallery to fit two, i.e. a 448px viewport -- so every phone got
   a single column. Capping at 170px keeps the tiles their usual size, and their usual
   count per row, as soon as there is room. `aspect-ratio` keeps them in proportion once
   the width is fluid; the height was hard-coded next to the width. */
.kz-embed-form .pictures-list {
  isolation: isolate; display: flex; flex-wrap: wrap; gap: 26px 20px;
  /* The gallery had 6px of air above and 4px below, and the first tile's delete button --
     which hangs 22px over the tile -- actually overlapped the label by 5px. The top margin
     therefore has to clear that overhang before it buys any daylight. */
  margin: 40px 0 32px;
}
.kz-embed-form .pictures-list .file_container {
  float: none; margin: 0; flex: 0 0 auto;
  width: min(170px, calc(50% - 10px)); height: auto; aspect-ratio: 170 / 152;
}
/* Ces tuiles sont peintes en blanc par la feuille de l'uploader, ce qui sur une page sombre
   donnait deux aplats eblouissants au milieu du panneau -- et le libelle, clair puisque le
   theme est sombre, devenait illisible dessus. Elles reprennent donc la couleur de surface,
   celle du panneau qui les entoure.

   Quatre classes, parce que la regle d'origine en a deux (`.pictures-list .file_container`)
   et que sa feuille est chargee APRES celle-ci : a specificite egale, c'est elle qui gagne.
   Le trait pointille et l'icone d'appareil photo restent tels quels, ils ressortent deja sur
   le fond sombre. */
.dark .kz-embed-form .pictures-list .file_container { background: var(--kz-color-surface); }

/* -------------------------------------------------------------------------
 * Account dashboard
 * ---------------------------------------------------------------------- */
.kz-dashboard { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: var(--kz-gap-lg); align-items: start; }
/* `minmax(0, 1fr)` and not `1fr`: a bare `1fr` is `minmax(auto, 1fr)`, whose automatic
   minimum is the CONTENT's minimum, so a single child that cannot shrink widens the whole
   column and the page scrolls sideways. The desktop rule above already guards against it;
   the mobile one did not, which is where it matters. */
@media (max-width: 800px) { .kz-dashboard { grid-template-columns: minmax(0, 1fr); } }

.kz-nav { display: flex; flex-direction: column; gap: 2px; padding: 8px; }
.kz-nav__link { display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-radius: var(--kz-radius); color: var(--kz-color-body); text-decoration: none; font-size: 14.5px; font-weight: 600; transition: var(--kz-transition); }
.kz-nav__link:hover { background: var(--kz-color-soft); color: var(--kz-color-ink); }
.kz-nav__link.is-active { background: var(--kz-color-primary-tint); color: var(--kz-color-primary); }
.kz-nav__link svg { width: 18px; height: 18px; flex: 0 0 auto; }

/* Compteur de messages non lus. `margin-left: auto` le pousse au bout de l'entree du menu,
   ou qu'il soit dans le libelle ; `min-width` avec un padding lateral plus faible que la
   moitie garde un rond pour « 3 » et une pastille pour « 12 » plutot qu'un ovale etroit. */
.kz-nav__count {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--kz-color-primary);
  color: var(--kz-color-on-primary);
  font-size: 11.5px;
  font-weight: 800;
  line-height: 1;
}
.kz-nav__link.is-active .kz-nav__count { background: var(--kz-color-primary); color: var(--kz-color-on-primary); }
.kz-quicklink .kz-nav__count { margin-left: auto; }
.kz-nav__sep { height: 1px; background: var(--kz-color-line); margin: 6px 12px; }

.kz-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--kz-gap); }
.kz-stat { padding: 20px; }
.kz-stat__value { font-family: var(--kz-font-display); font-weight: 800; font-size: 30px; color: var(--kz-color-ink); line-height: 1; }
.kz-stat__label { margin-top: 6px; font-size: 13.5px; color: var(--kz-color-subtle); }
.kz-stat__value--primary { color: var(--kz-color-primary); }
/* Clickable variant: the whole tile is an anchor to the screen it counts. The
   transparent border keeps a linked tile the same height as a static one. */
.kz-stat--link { display: block; color: inherit; text-decoration: none; border: 1.5px solid transparent; transition: var(--kz-transition); }
.kz-stat--link:hover, .kz-stat--link:focus-visible { border-color: var(--kz-color-primary); box-shadow: var(--kz-shadow-lg); }
.kz-stat--link:hover .kz-stat__label, .kz-stat--link:focus-visible .kz-stat__label { color: var(--kz-color-ink); }

.kz-quicklinks { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.kz-quicklink { display: flex; align-items: center; gap: 12px; padding: 16px; border-radius: var(--kz-radius); border: 1.5px solid var(--kz-color-line); background: var(--kz-color-surface); color: var(--kz-color-ink); text-decoration: none; font-family: var(--kz-font-display); font-weight: 700; font-size: 14.5px; transition: var(--kz-transition); }
.kz-quicklink:hover { border-color: var(--kz-color-primary); color: var(--kz-color-primary); box-shadow: var(--kz-shadow); }
.kz-quicklink svg { width: 22px; height: 22px; flex: 0 0 auto; color: var(--kz-color-primary); }

/* -------------------------------------------------------------------------
 * Manage list (my listings, my orders…)
 * ---------------------------------------------------------------------- */
.kz-btn--sm { height: 36px; padding-inline: 14px; font-size: 13px; }
.kz-btn--danger { color: var(--kz-color-danger); }
.kz-btn--danger:hover { border-color: var(--kz-color-danger); color: var(--kz-color-danger); }

.kz-page-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.kz-page-head__title { margin: 0; font-family: var(--kz-font-display); font-weight: 800; font-size: clamp(22px, 3vw, 28px); color: var(--kz-color-ink); }

.kz-manage-item { display: flex; gap: 16px; align-items: center; padding: 14px 0; border-bottom: 1px solid var(--kz-color-line); flex-wrap: wrap; }
.kz-manage-item:last-child { border-bottom: 0; }
.kz-manage-item__img { width: 88px; height: 66px; border-radius: var(--kz-radius); overflow: hidden; background: var(--kz-color-soft); flex: 0 0 auto; display: block; }
.kz-manage-item__img img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Search bar for dashboard lists (sales, orders). Pairs with sales-search.js,
   which needs #sales-search-input / #sales-search-clear to exist. */
.kz-searchbar { position: relative; display: flex; align-items: center; margin-bottom: 16px; }
.kz-searchbar__icon { position: absolute; left: 16px; width: 18px; height: 18px; color: var(--kz-color-subtle); pointer-events: none; }
.kz-searchbar__input { width: 100%; box-sizing: border-box; height: 48px; padding: 0 44px; font: 14.5px var(--kz-font); color: var(--kz-color-ink); background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius-pill); outline: none; transition: var(--kz-transition); }
.kz-searchbar__input::placeholder { color: var(--kz-color-subtle); }
.kz-searchbar__input:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }
.kz-searchbar__input::-webkit-search-cancel-button { display: none; }
.kz-searchbar__clear { position: absolute; right: 12px; display: none; align-items: center; justify-content: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--kz-color-subtle); cursor: pointer; }
.kz-searchbar__clear:hover { background: var(--kz-color-soft); color: var(--kz-color-ink); }
.kz-searchbar__clear svg { width: 15px; height: 15px; display: block; margin: auto; }
.kz-manage-item__body { flex: 1; min-width: 180px; display: flex; flex-direction: column; gap: 5px; align-items: flex-start; }
.kz-manage-item__title { font-family: var(--kz-font-display); font-weight: 700; font-size: 15px; color: var(--kz-color-ink); text-decoration: none; }
.kz-manage-item__title:hover { color: var(--kz-color-primary); }
.kz-manage-item__price { font-family: var(--kz-font-display); font-weight: 800; font-size: 15px; color: var(--kz-color-ink); white-space: nowrap; }
.kz-manage-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* -------------------------------------------------------------------------
 * Avatar cropbox (portable) — DOM hooks are driven by user-edit.js + cropperjs.
 * ---------------------------------------------------------------------- */
.kz-avatar { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.kz-avatar__view { display: flex; align-items: center; gap: 16px; }
.kz-avatar__preview { width: 96px; height: 96px; border-radius: 50%; overflow: hidden; background: var(--kz-color-soft); flex: 0 0 auto; }
.kz-avatar__preview img.user-avatar { width: 100%; height: 100%; object-fit: cover; display: block; }
.kz-avatar__cropper { width: 100%; max-width: 420px; }
.kz-avatar__stage { max-width: 360px; background: var(--kz-color-soft); border-radius: var(--kz-radius); overflow: hidden; }
.kz-avatar__stage img { display: block; max-width: 100%; }
.kz-avatar__zoom, .kz-avatar__actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.kz-avatar__loader { position: fixed; inset: 0; background: rgba(255, 255, 255, .6); z-index: 1200; display: grid; place-items: center; }
.dark .kz-avatar__loader { background: rgba(16, 20, 31, .6); }
.kz-spinner { width: 34px; height: 34px; border-radius: 50%; border: 3px solid var(--kz-color-line); border-top-color: var(--kz-color-primary); animation: kz-spin .7s linear infinite; }

/* Toggle helper used by legacy JS (user-edit.js adds/removes `.hidden`). */
.kz-embed-form .hidden, .kz-avatar .hidden, .hidden.kz-avatar__cropper, .hidden#js-avatar-box, #cropper-box.hidden, #js-avatar-box.hidden, #loader.hidden { display: none !important; }

/* Same fix, for vendor_settings.js (availability editor): it also toggles `.hidden` on elements
   that carry a system.css class setting `display` itself (flex), which would otherwise always win
   over Tailwind's `.hidden` - Tailwind's utility lives in `@layer utilities`, and any unlayered
   rule (system.css has no @layer) beats a layered one regardless of specificity or source order. */
#calendar-loader.hidden, .timeslots_container.hidden, .kz-avail-modal__timeslots.hidden { display: none !important; }

/* Accessibility helper */
.kz-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* -------------------------------------------------------------------------
 * Booking widget compatibility shim — wraps the shared AdvancedCalendar /
 * legacy Booking MiniForm cells (`.kz-booking-widget`, see listing.liquid).
 * Their own markup uses plain Bootstrap-lite classes (.btn, .form-control,
 * .alert) with no Bootstrap loaded on a portable theme, so this reskins just
 * those classes with the kz-* tokens. Scoped under .kz-booking-widget so it
 * never touches a real Bootstrap tenant.
 * ---------------------------------------------------------------------- */
.kz-booking-widget input[type="text"], .kz-booking-widget select, .kz-booking-widget .form-control {
  width: 100%; box-sizing: border-box; height: var(--kz-field-h); padding-inline: 16px;
  font: 15px var(--kz-font); color: var(--kz-color-ink); background: var(--kz-color-surface);
  border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius); outline: none;
  margin-bottom: 10px;
}
/* margin-inline is reset on purpose: the shared AdvancedCalendar submit ships as
   `class="btn btn-rounded btn-primary add-to-cart mx-5"`. `mx-5` is inert on a Bootstrap 3
   tenant, but a portable theme's Tailwind build is UNPREFIXED and auto-scans the PHP tree,
   so it really emits `.mx-5{margin-inline:1.25rem}` — which, on top of the width:100% below,
   pushed the button 20px right and 20px past the card, out of line with the ghost buttons
   under it. Same class-collision hazard the admin build avoids with its `tw` prefix.
   This wins because system.css is unlayered and Tailwind's utilities are in @layer
   utilities (see the .hidden note above). */
.kz-booking-widget .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  box-sizing: border-box; height: var(--kz-control-h); padding-inline: 22px; width: 100%;
  font-family: var(--kz-font-display); font-weight: 700; font-size: 14px; line-height: 1;
  border: 1.5px solid transparent; border-radius: var(--kz-radius-pill);
  cursor: pointer; text-decoration: none; user-select: none; transition: var(--kz-transition);
  background: var(--kz-color-primary); color: var(--kz-color-on-primary);
  margin-top: var(--kz-gap); margin-inline: 0;
}
.kz-booking-widget .btn:hover { background: var(--kz-color-primary-hover); }
.kz-booking-widget .btn.btn-default { background: var(--kz-color-surface); color: var(--kz-color-ink); border-color: var(--kz-color-line); width: auto; height: 36px; padding-inline: 14px; }
.kz-booking-widget .btn-xs { width: auto; height: 32px; padding-inline: 12px; font-size: 12.5px; }
.kz-booking-widget .alert { border-radius: var(--kz-radius); padding: 12px 16px; font-size: 14px; line-height: 1.5; margin-bottom: 12px; }
.kz-booking-widget .alert-danger, .kz-booking-widget .alert-warning { background: var(--kz-color-warning-tint); color: var(--kz-color-warning); }

/* -------------------------------------------------------------------------
 * Vendor availability editor (weekly schedule + specific-date overrides).
 * Reuses vendor_settings.js and FullCalendar unchanged (see
 * AdvancedCalendar/index.php's portable branch) - this only reskins the
 * surrounding markup, so every rule here is scoped to classes/ids that
 * script queries, never renamed.
 * ---------------------------------------------------------------------- */
.kz-availability-listing-banner { display: flex; align-items: center; gap: 10px; margin-bottom: var(--kz-gap); }
.kz-availability-listing-banner img { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }
.kz-availability-listing-banner a { font-family: var(--kz-font-display); font-weight: 700; color: var(--kz-color-ink); text-decoration: none; }
.kz-availability-listing-banner a:hover { color: var(--kz-color-primary); }

/* Schedule card chrome, per the availability mockup. */
.kz-availability-header__subtitle { margin: 4px 0 0; font-size: 13px; color: var(--kz-color-subtle); }

/* Segmented view switch. vendor_settings.js marks the ACTIVE side `disabled`, so the styling
   keys off that instead of a class the JS would also have to maintain. */
.kz-availability-view-toggle { display: inline-flex; padding: 4px; gap: 4px; background: var(--kz-color-soft); border-radius: var(--kz-radius-pill); }
.kz-availability-view-toggle__btn { border: 0; background: transparent; border-radius: var(--kz-radius-pill); padding: 7px 16px; font-family: var(--kz-font-display); font-weight: 600; font-size: 13px; color: var(--kz-color-subtle); cursor: pointer; transition: background .15s ease, color .15s ease; }
.kz-availability-view-toggle__btn:hover:not([disabled]) { color: var(--kz-color-ink); }
.kz-availability-view-toggle__btn[disabled] { background: var(--kz-color-surface); color: var(--kz-color-ink); box-shadow: var(--kz-shadow); cursor: default; }

/* Day enable control drawn as a switch rather than a bare checkbox. */
.kz-availability-day__toggle input[type="checkbox"].day_checkbox { appearance: none; -webkit-appearance: none; flex: none; width: 40px; height: 22px; border-radius: var(--kz-radius-pill); background: var(--kz-color-line); position: relative; cursor: pointer; transition: background .15s ease; }
.kz-availability-day__toggle input[type="checkbox"].day_checkbox::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(17, 24, 39, .25); transition: transform .15s ease; }
.kz-availability-day__toggle input[type="checkbox"].day_checkbox:checked { background: var(--kz-color-primary); }
.kz-availability-day__toggle input[type="checkbox"].day_checkbox:checked::after { transform: translateX(18px); }

.kz-availability-day__actions { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.kz-availability-day__add { border: 0; background: transparent; padding: 0; font-size: 13px; font-weight: 600; color: var(--kz-color-primary); cursor: pointer; }
.kz-availability-day__add:hover { text-decoration: underline; }

/* Sits on the day's header line, right-aligned, as drawn. Greyed out when the day is closed:
   there are no hours to copy, and :has() spares the JS having to keep a disabled state in sync. */
.kz-availability-day__copy { margin-left: auto; border: 1px solid var(--kz-color-line); background: var(--kz-color-surface); border-radius: var(--kz-radius-pill); padding: 7px 16px; font-family: var(--kz-font-display); font-weight: 600; font-size: 13px; color: var(--kz-color-body); cursor: pointer; }
.kz-availability-day__copy:hover { border-color: var(--kz-color-primary); color: var(--kz-color-primary); }
.kz-availability-day:has(.day_checkbox:not(:checked)) .kz-availability-day__copy { opacity: .45; pointer-events: none; }

/* Specific dates ("Dates particulières") - the rows themselves are styled in the
   #specific_timeslots_container block further down, which is scoped by id and wins. */
.kz-availability-exceptions__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.kz-availability-exceptions__title { margin: 0 0 4px; font-family: var(--kz-font-display); font-weight: 700; font-size: 15px; color: var(--kz-color-ink); }

/* Summary tiles above the schedule: open days, weekly hours, upcoming exceptions. */
.kz-availability-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--kz-gap); margin-bottom: var(--kz-gap-lg); }
@media (max-width: 640px) { .kz-availability-stats { grid-template-columns: 1fr; } }
.kz-availability-stat { background: var(--kz-color-surface); border: 1px solid var(--kz-color-line); border-radius: var(--kz-radius); padding: 14px 16px; }
.kz-availability-stat__label { display: block; font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--kz-color-subtle); }
.kz-availability-stat__value { display: block; margin-top: 6px; font-family: var(--kz-font-display); font-weight: 700; font-size: 24px; line-height: 1.1; color: var(--kz-color-ink); }
.kz-availability-stat__value small { font-size: 15px; font-weight: 400; color: var(--kz-color-subtle); }

.kz-availability-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.kz-availability-tz { display: block; margin-top: 4px; font-size: 12.5px; color: var(--kz-color-subtle); }
.kz-availability-view-toggle { display: flex; gap: 8px; }

.kz-availability-grid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--kz-gap-lg); align-items: start; margin-top: var(--kz-gap); }
@media (max-width: 800px) { .kz-availability-grid { grid-template-columns: 1fr; } }

/* Three columns as drawn: day name | hours and actions | copy button. */
.kz-availability-day { display: grid; grid-template-columns: minmax(140px, 190px) 1fr auto; column-gap: 16px; align-items: center; padding: 14px 16px; margin-bottom: 10px; border: 1px solid var(--kz-color-line); border-radius: var(--kz-radius); }
/* Same clamp, same reason: this is the track the day's time selects sit in. */
@media (max-width: 720px) { .kz-availability-day { grid-template-columns: minmax(0, 1fr); row-gap: 10px; } }
.kz-availability-day:last-child { margin-bottom: 0; }
/* A closed day is dimmed rather than hidden, so the week still reads as seven rows. */
.kz-availability-day:has(.day_checkbox:not(:checked)) { background: var(--kz-color-page); }
.kz-availability-day__row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.kz-availability-day__toggle { display: flex; align-items: center; gap: 10px; font-family: var(--kz-font-display); font-weight: 700; font-size: 14px; color: var(--kz-color-ink); cursor: pointer; }
.kz-availability-day__toggle input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--kz-color-primary); margin: 0; }
/* Deliberately lighter weight than the day toggle above - it's a secondary, rarer action (block
   the whole day from every booking) and must read as visually subordinate to "day enabled", not
   compete with it on the same line. See vendor_settings.js's handleDayCheckbox/handleBlockedWeekdayCheckbox
   for why the two are mutually exclusive (radio-like). */
.kz-availability-day__block-toggle { font-family: var(--kz-font); font-weight: 600; font-size: 12.5px; color: var(--kz-color-subtle); }
.kz-availability-day__block-toggle input[type="checkbox"] { accent-color: var(--kz-color-danger); }
.kz-availability-day__body { margin-top: 10px; }
.kz-availability-day .availability-hours-cell { min-width: 220px; }
.kz-availability-day .availability-hours-cell.timeslots_container { display: flex; flex-direction: column; gap: 8px; }
.kz-availability-day__unavailable { color: var(--kz-color-muted); font-size: 13.5px; }

.kz-availability-timeslot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* The plugin's own advanced_calendar_style.css centres these rows and loads after this
   file, which pushed the selects out of line with "Add a time slot" underneath. Compound
   selector to win on specificity, and scoped to the portable class so the Bootstrap themes
   keep the centring they were built around. */
/* `flex-wrap: wrap` again, and it takes TWO classes to say it. The plugin's own stylesheet
   sets `flex-flow: row nowrap` on .availability-hours-timeslot and is linked after this file,
   so at equal specificity it won: the four time selects, the arrow and the delete button were
   pinned on one line 328px wide inside a 345px card, and the whole page scrolled sideways on a
   phone. Measured at 320, 360, 375 and 393px — the row now wraps and the document is exactly
   as wide as the screen at every one of them.

   Two classes rather than reordering the links: the plugin sheet is loaded by the template
   itself, and legacy themes still want that nowrap, which this selector leaves them. */
.kz-availability-timeslot.availability-hours-timeslot { justify-content: flex-start; flex-wrap: wrap; }
/* Same stylesheet also puts a 10px side margin on each bound, which offset the first one
   from the edge; the row's own gap already handles the spacing between them. */
.kz-availability-timeslot.availability-hours-timeslot > .kz-availability-timeslot-range { margin: 0; }
.kz-availability-timeslot-range { display: flex; align-items: center; gap: 4px; }
.kz-availability-timeslot-arrow { color: var(--kz-color-muted); }
.kz-availability-timeslot-remove { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: 50%; background: var(--kz-color-soft); color: var(--kz-color-subtle); cursor: pointer; }
.kz-availability-timeslot-remove:hover { background: var(--kz-color-danger-tint); color: var(--kz-color-danger); }
.kz-availability-time-select { height: 38px; padding-inline: 8px; font: 13.5px var(--kz-font); color: var(--kz-color-ink); background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius-sm); outline: none; }
.kz-availability-time-select:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }

/* Month grid: the four states of the mockup's legend. Painted as classes on the day cell by
   paintDayStates() in the availability template, replacing the old inline green/red background. */
/* Un jour ouvre l'editeur d'exception au clic (dateClick, vendor_settings.js) : le curseur doit le
   dire, sinon la grille ressemble a un simple affichage. Les jours hors plage ne repondent pas au
   clic et gardent donc le curseur par defaut. */
#calendar-view td.fc-daygrid-day { cursor: pointer; }
#calendar-view td.fc-daygrid-day.fc-day-disabled { cursor: default; }
#calendar-view td.fc-daygrid-day.kz-day--open { background: var(--kz-color-success-tint); }
#calendar-view td.fc-daygrid-day.kz-day--exception { background: var(--kz-color-primary-tint); }
#calendar-view td.fc-daygrid-day.kz-day--closed-exception { background: var(--kz-color-danger-tint); }
#calendar-view td.fc-daygrid-day.kz-day--closed { background: var(--kz-color-soft); }
#calendar-view .kz-day__summary { padding: 2px 4px; font-size: 12px; font-weight: 600; text-align: center; color: var(--kz-color-subtle); }
#calendar-view td.kz-day--closed-exception .kz-day__summary { color: var(--kz-color-danger); }

/* Plusieurs creneaux dans une meme journee. Sur un ecran large, la case a la place de les
   empiler : c'est ce qu'un vendeur veut lire, plutot qu'un "2 creneaux" qui l'oblige a ouvrir
   la journee pour savoir lesquels (LST-83). La hauteur du harness redevient automatique -- la
   feuille du plugin la fixe a 100% pour qu'un creneau unique remplisse la case et reste
   cliquable partout, ce qui ferait deborder deux creneaux empiles.

   Les deux rendus sont dans le DOM et c'est la largeur qui tranche : pas de JavaScript au
   redimensionnement, et une rotation de telephone ne peut pas laisser le mauvais affiche. */
#calendar-view td.kz-day--multi .fc-daygrid-event-harness { height: auto; }
#calendar-view td.kz-day--multi .kz-day__summary { display: none; }

/* Sur un telephone, "N creneaux" ne tient pas : le mot seul demande 66px dans une case de 50px
   a 390px, et 41px a 320px -- il n'existe aucune taille a la fois lisible et assez petite. Il
   debordait donc sur la case voisine, dans les deux modes. Le nombre suffit a cette largeur, le
   libelle complet reste dans l'attribut title et dans le DOM pour qui l'affiche plus large.

   Le libelle "Ferme" debordait pour la meme raison, sans mot a retirer : a 12px il demande 41px
   dans une case de 40px a 320px. La taille descend donc a 10.5px, celle deja donnee au titre des
   creneaux a cette largeur, et les trois libelles tiennent enfin dans leur case.

   Hors mode sombre : le debordement n'avait rien a voir avec la couleur. */
@media (max-width: 640px) {
  #calendar-view .kz-day__summary { padding: 2px; font-size: 10.5px; }
  #calendar-view .kz-day__summary-word { display: none; }
  /* Une case de 50px ne tient pas deux "09:00 - 12:00", qui y prennent deja deux lignes
     chacun : a cette largeur le compte reste la seule lecture possible. */
  #calendar-view td.kz-day--multi .fc-daygrid-event-harness { display: none; }
  #calendar-view td.kz-day--multi .kz-day__summary { display: block; }
}

.kz-day-legend { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 12px; font-size: 12.5px; color: var(--kz-color-subtle); }
.kz-day-legend__item { display: inline-flex; align-items: center; gap: 6px; }
.kz-day-legend__dot { width: 12px; height: 12px; border-radius: 4px; border: 1px solid var(--kz-color-line); }
.kz-day-legend__dot--open { background: var(--kz-color-success-tint); }
.kz-day-legend__dot--exception { background: var(--kz-color-primary-tint); }
.kz-day-legend__dot--closed-exception { background: var(--kz-color-danger-tint); }
.kz-day-legend__dot--closed { background: var(--kz-color-soft); }

/* Mode sombre du calendrier (LST-75).

   FullCalendar peint lui-meme un fond BLANC sur les sections collantes de sa grille -- c'est
   ce qui empeche les lignes de transparaitre au defilement -- et sa feuille est injectee par
   son propre script. Sur une page sombre, cela donne une bande blanche en haut de la grille.
   Trois classes ici, une de plus que la sienne, donc la regle passe quel que soit l'ordre
   d'injection.

   Les jours ouverts ne portent PAS l'aplat : c'est le creneau qui prend la couleur, en
   pastille, et la case garde le fond de la page. Un aplat plein sur toute la case ecrasait la
   grille et laissait par-dessus le creneau en bleu FullCalendar -- deux couleurs pour une
   seule information, alors que la pastille ne peint que ce qui est effectivement renseigne.
   `background: transparent` plutot que la suppression de la regle : la regle de base peint la
   teinte verte, il faut donc la neutraliser ici et pas seulement s'abstenir.

   Le resume "N creneaux" remplace les creneaux des qu'il y en a plusieurs (paintDayStates les
   masque), donc il porte la meme pastille : sans cela un jour a trois horaires paraitrait vide
   quand un jour a un seul horaire serait colore. Limite a .kz-day--open, car le meme element
   sert au libelle "Ferme" des jours fermes exceptionnellement, qui doit rester rouge.

   La bordure suit le fond : FullCalendar en dessine une autour de l'evenement et, laissee a sa
   couleur, elle cerclerait la pastille de bleu. Les jours d'exception gardent leur TEINTE
   primaire sur la case, les deux etats restent donc distincts.

   Les jours qui ont des creneaux reprennent un aplat, mais clair : #fbe7f2, avec le texte en
   #e0237a -- les deux valeurs viennent du client, capture a l'appui. La pastille par creneau,
   essayee avant, revenait a colorer une etiquette au lieu d'un etat : la case se lit d'un coup
   d'oeil, la pastille demandait de chercher.

   Ces deux couleurs sont posees en variables plutot qu'ecrites dans les regles : ce sont des
   litteraux, pas des jetons de la marketplace, et un autre theme portable qui n'est pas rose
   n'aura qu'a les redefinir dans son propre bloc .dark au lieu d'aller reecrire ces selecteurs.

   Le contraste mesure est de 3.93:1. C'est au-dessus du seuil AA pour du grand texte, en
   dessous des 4.5:1 demandes pour du petit -- valeurs choisies par le client, signalees telles
   quelles.

   Les creneaux perdent leur cadre : FullCalendar leur dessine un fond et une bordure bleus, qui
   sur un aplat n'apportent rien et ramenaient la couleur dont on voulait sortir. Ils redeviennent
   du texte, colore par l'etat de leur case -- rose sur un jour ouvert, encre claire ailleurs,
   sinon le rose passerait a 2.3:1 sur la teinte sombre des jours d'exception.

   Le numero du jour suit le texte : en `--kz-color-ink`, pense pour un fond sombre, il devenait
   illisible sur l'aplat clair. */
.dark .fc .fc-scrollgrid-section-sticky > * { background: transparent; }

.dark #calendar-view .fc-daygrid-event { background: transparent; border-color: transparent; }
.dark #calendar-view .fc-event-title { color: var(--kz-color-ink); }

.dark #calendar-view td.fc-daygrid-day.kz-day--open { background: var(--kz-calendar-open-bg); }
.dark #calendar-view td.kz-day--open .fc-event-title,
.dark #calendar-view td.kz-day--open .kz-day__summary,
.dark #calendar-view td.kz-day--open .fc-daygrid-day-number { color: var(--kz-calendar-open-ink); }
.dark .kz-day-legend__dot--open { background: var(--kz-calendar-open-bg); }

.kz-availability-loader { display: flex; justify-content: center; padding: 40px 0; }

/* Server-rendered AND JS-created (vendor_settings.js addSpecificAvailability()) both use these
   exact class names - scoped here rather than left as bare global .label/.specific_remove. */
#specific_timeslots_container .specific_timeslot_row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 12px; border: 1px solid var(--kz-color-line); border-radius: var(--kz-radius); font-size: 13.5px; color: var(--kz-color-body); }
/* The date reads as the row's title, not as a chip - the `.label`/`.label-primary` classes are
   only still on it because the Bootstrap themes share this markup. */
#specific_timeslots_container .label { flex: 1 1 auto; padding: 0; border-radius: 0; background: none; font-family: var(--kz-font-display); font-weight: 700; font-size: 13.5px; color: var(--kz-color-ink); text-transform: capitalize; }
#specific_timeslots_container .label-primary { background: none; color: var(--kz-color-ink); }
#specific_timeslots_container .kz-availability-exception__badge { flex: none; padding: 3px 10px; border-radius: var(--kz-radius-pill); font-size: 12px; font-weight: 600; white-space: nowrap; }
#specific_timeslots_container .kz-availability-exception__badge--closed { background: var(--kz-color-danger-tint); color: var(--kz-color-danger); }
#specific_timeslots_container .kz-availability-exception__badge--hours { background: var(--kz-color-success-tint); color: var(--kz-color-success); }
#specific_timeslots_container .specific_remove, #specific_timeslots_container .specific_calendar { margin-left: auto; cursor: pointer; color: var(--kz-color-subtle); }
#specific_timeslots_container .specific_remove:hover { color: var(--kz-color-danger); }

/* Modal (native show/hide via inline style.display, not a .is-open class - see
   vendor_settings.js's initModal()/initOverrideButton() - so the backdrop is painted directly on
   the #tinycalModal wrapper itself rather than a separate overlay child, matching the
   `event.target === modal` outside-click check the script does). */
.kz-avail-modal { position: fixed; inset: 0; z-index: 1000; display: none; background: rgba(17, 24, 39, .55); }
.kz-avail-modal__dialog { position: relative; max-width: 600px; width: calc(100% - 40px); margin: 6vh auto; background: var(--kz-color-surface); color: var(--kz-color-ink); border-radius: var(--kz-radius-lg); box-shadow: var(--kz-shadow-lg); max-height: 88vh; overflow: auto; box-sizing: border-box; }
.kz-avail-modal__header { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--kz-color-line); }
.kz-avail-modal__title { margin: 0; font-family: var(--kz-font-display); font-weight: 700; font-size: 16px; color: var(--kz-color-ink); }
.kz-avail-modal__close { flex: 0 0 auto; border: 0; background: var(--kz-color-soft); width: 32px; height: 32px; border-radius: 50%; cursor: pointer; color: var(--kz-color-subtle); font-size: 16px; line-height: 1; }
.kz-avail-modal__close:hover { color: var(--kz-color-ink); }
.kz-avail-modal__body { padding: 20px; }
.kz-avail-modal__selected-days { margin-top: 14px; font-size: 14px; font-weight: 600; color: var(--kz-color-ink); }
.kz-avail-modal__hint { margin: 6px 0 0; font-size: 12.5px; font-weight: 500; color: var(--kz-color-subtle); background: var(--kz-color-soft); border-radius: var(--kz-radius-sm); padding: 8px 10px; }
.kz-avail-modal__timeslots { margin-top: 14px; display: flex; flex-direction: column; gap: 12px; }
#tinycal_override_slots_block { display: flex; flex-direction: column; gap: 10px; }
.kz-avail-modal__unavailable-toggle {
  display: inline-flex; align-items: center; gap: 8px; align-self: flex-start;
  height: 36px; padding-inline: 14px; font: 600 13px var(--kz-font);
  color: var(--kz-color-danger); background: var(--kz-color-surface);
  border: 1.5px solid var(--kz-color-danger); border-radius: var(--kz-radius-pill);
  cursor: pointer; transition: background-color .15s, color .15s;
}
.kz-avail-modal__unavailable-toggle:hover { background: var(--kz-color-danger-tint); }
.kz-avail-modal__unavailable-toggle.is-active { background: var(--kz-color-danger); color: #fff; }
/* "Remove this override" - only shown for a selection that already has one (see renderOverrideForm()
   in vendor_settings.js); kept visually quiet (plain underlined text) since it's a secondary,
   destructive-but-scoped action next to the toggle/slots it applies to, not a primary button. */
.kz-avail-modal__remove-override { align-self: flex-start; background: none; border: 0; padding: 2px 0; font: 600 12.5px var(--kz-font); color: var(--kz-color-danger); cursor: pointer; text-decoration: underline; }
.kz-avail-modal__remove-override:hover { opacity: .75; }
/* Mini date-picker calendar inside the modal (#tinycal) - a small dot flags days that already have
   a saved override, so the vendor can tell before clicking; see fetchExistingOverrides() /
   dayCellDidMount in vendor_settings.js. */
#tinycal .fc-daygrid-day.kz-has-override .fc-daygrid-day-number::after {
  content: ''; display: block; width: 5px; height: 5px; margin: 2px auto 0;
  border-radius: 50%; background: var(--kz-color-primary);
}
.kz-avail-modal__footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 16px 20px; border-top: 1px solid var(--kz-color-line); }
.kz-avail-modal__footer-actions { display: flex; gap: 8px; }

/* ---------------------------------------------------------------------
 * Blur editor (driven by Kr.blurEditor, /assets/js/blur-editor.js) — manual
 * license-plate/region blurring on the photo upload flow. Wider dialog than
 * the base .kz-modal since it hosts an interactive canvas.
 * ---------------------------------------------------------------------- */
.kz-blur-editor-modal .kz-modal__dialog { max-width: 760px; }
.kz-blur-editor__hint { margin: 0 0 14px; font-size: 13.5px; color: var(--kz-color-subtle); }
.kz-blur-editor__canvas-wrap {
  display: flex; justify-content: center; align-items: center; background: var(--kz-color-soft);
  border: 1px solid var(--kz-color-line); border-radius: var(--kz-radius); padding: 12px; overflow: auto;
}
/* No CSS scaling of the canvas: blur-editor.js now fits it to the container, and a
   canvas resized by the stylesheet keeps its own coordinate space, so Fabric would map
   a tap to the wrong place. The rule that was here also constrained width without
   height, which is what stretched the photo on a phone. */
.kz-blur-editor__canvas-wrap canvas { max-width: none; }
.kz-blur-editor__toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.kz-blur-editor__footer { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--kz-color-line); }

/* -------------------------------------------------------------------------
 * Filtre de prix (tranches rapides + champs)
 *
 * Le filtre se réduisait à deux `input[type=number]` nus, identiques au pixel
 * près sur mobile et sur desktop : ni symbole monétaire, ni indication de la
 * fourchette réelle — pourtant connue et déjà présente dans les attributs
 * min/max — et aucune application automatique, contrairement aux cases à
 * cocher. On saisissait donc deux montants à l'aveugle, puis rien ne se
 * passait tant que « Filtrer » n'était pas pressé.
 * ---------------------------------------------------------------------- */
.dg-price-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.dg-price-chip {
  border: 1.5px solid var(--kz-color-line); background: var(--kz-color-surface);
  color: var(--kz-color-body); border-radius: var(--kz-radius-pill, 999px);
  padding: 7px 14px; font-size: 13px; font-weight: 600; line-height: 1.2;
  cursor: pointer; white-space: nowrap; transition: var(--kz-transition);
}
.dg-price-chip:hover { border-color: var(--kz-color-primary); color: var(--kz-color-primary); }
.dg-price-chip.is-active {
  background: var(--kz-color-primary); border-color: var(--kz-color-primary);
  color: var(--kz-color-on-primary);
}

.dg-price-fields { display: flex; gap: 8px; align-items: center; }
.dg-price-sep { color: var(--kz-color-muted); }
/* Le suffixe € vit DANS le champ : posé à côté, il se décrochait dès que la
   colonne se rétrécissait. */
.dg-price-field { position: relative; flex: 1; min-width: 0; display: block; }
.dg-price-field > span {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  color: var(--kz-color-muted); font-size: 13px; pointer-events: none;
}
.dg-price-field input { width: 100%; padding-right: 28px; }
/* Les flèches d'incrément n'ont pas de sens sur un montant : monter un prix de
   1 € à la fois n'aide personne et elles mangent la place du suffixe. */
.dg-price-field input::-webkit-outer-spin-button,
.dg-price-field input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.dg-price-field input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* =========================================================================
   Inbox — two panes, list on the left, open thread on the right.

   Rendered by the Inbox plugin's portable branch (Conversations/index +
   show), so the classes live here rather than in a theme: every portable
   theme gets the same messaging screen and only the --kz-* tokens change.
   Below 900px the two panes stack, and the list is hidden while a thread is
   open — on a phone a conversation is a page, not a column.
   ========================================================================= */
.kz-inbox {
    display: grid;
    grid-template-columns: minmax(240px, 320px) 1fr;
    gap: 18px;
    align-items: start;
}
.kz-inbox__list {
    background: var(--kz-color-surface);
    border: 1px solid var(--kz-color-line);
    border-radius: var(--kz-radius-card, 14px);
    overflow: hidden;
}
.kz-inbox__row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    text-decoration: none;
    color: inherit;
    border-bottom: 1px solid var(--kz-color-line);
}
.kz-inbox__row:last-child { border-bottom: 0; }
.kz-inbox__row:hover { background: var(--kz-color-soft); }
.kz-inbox__row.is-active { background: var(--kz-color-soft); }
.kz-inbox__row.is-unread .kz-inbox__name { font-weight: 700; }
.kz-inbox__avatar {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
}
.kz-inbox__avatar--empty { display: inline-block; background: var(--kz-color-primary-tint, #EEF0F4); }
.kz-inbox__row-body { min-width: 0; flex: 1 1 auto; display: block; }
.kz-inbox__row-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.kz-inbox__name { font-weight: 600; font-size: 14.5px; color: var(--kz-color-ink); }
.kz-inbox__time { font-size: 12.5px; color: var(--kz-color-subtle); flex: 0 0 auto; }
.kz-inbox__snippet {
    display: block;
    font-size: 13.5px;
    color: var(--kz-color-subtle);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.kz-inbox__badge {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--kz-color-primary);
    flex: 0 0 auto;
}
.kz-inbox__thread {
    display: flex;
    flex-direction: column;
    min-height: 420px;
    background: var(--kz-color-surface);
    border: 1px solid var(--kz-color-line);
    border-radius: var(--kz-radius-card, 14px);
    overflow: hidden;
}
.kz-inbox__thread-head {
    display: flex;
    align-items: center;
    gap: 12px;
    /* This is a <header>, and the theme sets `margin-bottom: 40px !important` on
       that element. Inside the thread it must not: the band of empty surface it
       opened between the participant and the listing banner read as a rendering
       bug. !important is required to outrank the theme's own !important. */
    margin: 0 !important;
    padding: 16px 18px;
    border-bottom: 1px solid var(--kz-color-line);
}
.kz-inbox__meta { display: block; font-size: 13px; color: var(--kz-color-subtle); }
.kz-inbox__listing {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 18px;
    text-decoration: none;
    color: inherit;
    background: var(--kz-color-soft);
    border-bottom: 1px solid var(--kz-color-line);
}
.kz-inbox__listing-img { width: 56px; height: 56px; border-radius: 10px; object-fit: cover; flex: 0 0 auto; }
.kz-inbox__listing-body { flex: 1 1 auto; min-width: 0; }
.kz-inbox__listing-title { display: block; font-weight: 600; font-size: 14.5px; color: var(--kz-color-ink); }
.kz-inbox__ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; color: var(--kz-color-subtle); }
.kz-inbox__listing-price { font-weight: 700; font-size: 15px; flex: 0 0 auto; color: var(--kz-color-ink); }
.kz-inbox__messages { flex: 1 1 auto; padding: 18px; display: flex; flex-direction: column; gap: 12px; }
.kz-inbox__bubble-row { display: flex; flex-direction: column; align-items: flex-start; max-width: 76%; }
.kz-inbox__bubble-row.is-mine { align-self: flex-end; align-items: flex-end; }

/* Avatar de l'expediteur a cote de chaque message. La rangee ne devient une LIGNE que
   lorsqu'il y a un avatar a poser : le fil des commandes (Order.Message) emet les memes
   bulles sans avatar et doit rester en colonne. La vignette est calee en HAUT de la bulle :
   alignee en bas, elle descendait au niveau de l'heure — sous la bulle — et se detachait du
   message. `row-reverse` met l'avatar apres la bulle pour ses propres messages, du cote ou
   l'oeil les lit deja. */
.kz-inbox__bubble-row--avatar { flex-direction: row; align-items: flex-start; gap: 8px; }
.kz-inbox__bubble-row--avatar.is-mine { flex-direction: row-reverse; }
.kz-inbox__bubble-col { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
.kz-inbox__bubble-row.is-mine .kz-inbox__bubble-col { align-items: flex-end; }
.kz-inbox__msg-avatar {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--kz-color-soft);
}
.kz-inbox__bubble {
    padding: 11px 15px;
    border-radius: 14px;
    background: var(--kz-color-soft);
    font-size: 14.5px;
    line-height: 1.5;
    color: var(--kz-color-ink);
}
.kz-inbox__bubble-row.is-mine .kz-inbox__bubble {
    background: var(--kz-color-primary);
    color: var(--kz-color-on-primary);
}
.kz-inbox__bubble-time { margin-top: 4px; font-size: 12px; color: var(--kz-color-subtle); }
.kz-inbox__bubble-author { font-size: 12.5px; font-weight: 600; color: var(--kz-color-subtle); margin-bottom: 4px; }

/* Order thread: the same bubbles as the Inbox, embedded in a card rather than
   filling a column, so it needs its own bounds and its own composer spacing. */
.kz-order-thread { padding: 0; margin: 16px 0 18px; max-height: 420px; overflow-y: auto; }
.kz-order-thread__composer textarea { width: 100%; box-sizing: border-box; min-height: 110px; padding: 12px 16px; margin-bottom: 12px; font: 15px var(--kz-font); line-height: 1.5; color: var(--kz-color-ink); background: var(--kz-color-surface); border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius); outline: none; resize: vertical; }
.kz-order-thread__composer textarea:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }

/* Shared order attachments. */
/* `hidden` is the utility the legacy plugin JS toggles (AdvancedCalendar's list /
   calendar switch, among others). Tailwind does define it, but inside
   `@layer utilities` — and an unlayered rule beats ANY layered one whatever the
   specificity. FullCalendar injects an unlayered `.fc { display: flex }` at
   runtime, so `hidden` stopped hiding the calendar once it had been mounted
   once: coming back to the list view left the month grid below it. Unlayered
   and !important so the class means what its name says wherever it is used. */
.hidden { display: none !important; }

.kz-is-hidden { display: none !important; }

/* Bootstrap check markup emitted by the shared profile form. Unstyled, the
   wrapper stays `display:block`, so the box lands on its own line above its
   label, and the native control keeps the browser's light chrome — which reads
   as a white square on a dark theme. */
.form-check { display: flex; align-items: center; gap: 10px; padding: 5px 0; }
/* The attribute selector in `.kz-embed-form input[type="checkbox"] { width: auto }`
   counts as a class, so that rule is (0,2,1) — a plain `.form-check-input`
   loses to it and the box renders at the native 13px against a 17px height. */
.form-check .form-check-input[type="checkbox"] { flex: 0 0 auto; width: 17px; height: 17px; margin: 0; accent-color: var(--kz-color-primary); cursor: pointer; }
.form-check-label { margin: 0; font-weight: 500; font-size: 14.5px; color: var(--kz-color-body); cursor: pointer; }

/* Legacy Bootstrap dialogs (`data-toggle="modal"`). Bootstrap's own CSS is what
   keeps `.modal` hidden until it opens; without it the block simply rendered
   inline, which is how the account-deletion dialog ended up spilled across the
   profile page. The shim in the theme runtime toggles `is-open`. */
.modal { display: none; position: fixed; inset: 0; z-index: 1100; overflow-y: auto; background: rgba(17, 24, 39, .55); }
.modal.is-open { display: block; }
.modal .modal-dialog { position: relative; max-width: 520px; width: calc(100% - 40px); margin: 8vh auto; }
.modal .modal-content { background: var(--kz-color-surface); color: var(--kz-color-ink); border-radius: var(--kz-radius-lg); box-shadow: var(--kz-shadow-lg); overflow: hidden; }
.modal .modal-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--kz-color-line); }
.modal .modal-title { margin: 0; font-family: var(--kz-font-display); font-weight: 700; font-size: 17px; color: var(--kz-color-ink); }
.modal .modal-body { padding: 20px; color: var(--kz-color-body); font-size: 14.5px; line-height: 1.55; }
.modal .modal-footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 10px; padding: 16px 20px; border-top: 1px solid var(--kz-color-line); }
.modal .close { flex: 0 0 auto; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%; background: var(--kz-color-soft); color: var(--kz-color-subtle); font-size: 17px; line-height: 1; cursor: pointer; }
.modal .close:hover { color: var(--kz-color-ink); }

/* The account-deletion dialog ships its own stylesheet, written for a light
   theme: a #f9fafb warning box and a white cancel button read as bright blocks
   on a dark surface, and the destructive action is the quiet one of the two.
   Remap it onto the tokens and give the hierarchy back to the delete button. */
.modal .tw_warning_box { background: var(--kz-color-soft); border-left-color: var(--kz-color-danger); }
.modal .tw_warning_text, .modal .tw_modal_text, .modal .tw_confirmation_label { color: var(--kz-color-body); }
.modal .tw_warning_text strong, .modal .tw_confirmation_label strong { color: var(--kz-color-ink); }
.modal .tw_confirmation_input {
  box-sizing: border-box; padding: 11px 14px; font: 14.5px var(--kz-font);
  color: var(--kz-color-ink); background: var(--kz-color-surface);
  border: 1.5px solid var(--kz-color-line); border-radius: var(--kz-radius); outline: none;
}
.modal .tw_confirmation_input:focus { border-color: var(--kz-color-primary); box-shadow: var(--kz-ring); }
.modal .tw_confirmation_input::placeholder { color: var(--kz-color-subtle); }
.modal .tw_btn {
  display: inline-flex; align-items: center; justify-content: center;
  height: var(--kz-control-h); padding-inline: 22px; border-radius: var(--kz-radius-pill);
  font-family: var(--kz-font-display); font-weight: 700; font-size: 14px; line-height: 1;
  border: 1.5px solid transparent; cursor: pointer; text-decoration: none; transition: var(--kz-transition);
}
.modal .tw_btn_cancel { background: var(--kz-color-surface); color: var(--kz-color-ink); border-color: var(--kz-color-line); }
.modal .tw_btn_cancel:hover { background: var(--kz-color-soft); color: var(--kz-color-ink); }
.modal .tw_btn_delete { background: var(--kz-color-danger); color: #fff !important; border-color: var(--kz-color-danger); }
.modal .tw_btn_delete:hover { filter: brightness(1.1); color: #fff !important; }
/* The plugin marks the disabled state with !important on a light grey; match it
   with the theme's own muted surface rather than a block of near-white. */
.modal .tw_btn_disabled,
.modal .tw_btn_disabled:hover {
  background-color: var(--kz-color-soft) !important;
  color: var(--kz-color-subtle) !important;
  border-color: var(--kz-color-line) !important;
  opacity: .75;
}
.kz-attach__hint { margin: 0 0 16px; color: var(--kz-color-subtle); font-size: 14px; }
.kz-attach__form { margin: 0 0 20px; }
.kz-attach__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
.kz-attach__row { margin-bottom: 16px; }
.kz-attach__filehead { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 10px; }
.kz-attach__filehead .kz-label { margin-bottom: 0; }
.kz-attach__switch { padding: 0; border: 0; background: none; font: 600 13px var(--kz-font); color: var(--kz-color-primary); cursor: pointer; text-decoration: underline; }
.kz-attach__form input[type="file"] { height: auto; padding: 10px 14px; margin-top: 6px; }
.kz-attach__actions { display: flex; justify-content: flex-end; }
.kz-attach__empty { margin: 0; color: var(--kz-color-subtle); font-size: 14.5px; }
.kz-attach__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--kz-color-line); }
.kz-attach__item { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--kz-color-line); }
.kz-attach__item-body { flex: 1 1 220px; min-width: 0; }
.kz-attach__item-title { display: block; font-weight: 700; font-size: 14.5px; color: var(--kz-color-ink); }
.kz-attach__item-meta { font-size: 13px; color: var(--kz-color-subtle); }
.kz-attach__item-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.kz-inbox__empty { margin: auto; color: var(--kz-color-subtle); font-size: 14.5px; text-align: center; }
.kz-inbox__list-empty { margin: 0; padding: 20px 16px; color: var(--kz-color-subtle); font-size: 13.5px; line-height: 1.5; }
.kz-inbox__rating { display: block; margin-top: 2px; }

/* Attachment control in the composer: the native input is taken out of the flow
   and the label carries the affordance, so the picker still opens on click. */
.kz-attach-field { display: flex; align-items: center; margin: 10px 0 14px; }
/* `.kz-embed-form label { display: block }` sits at the same weight and later in
   the file, so this needs the extra class to stay a row. */
.kz-attach-field .kz-attach-btn { display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px; border: 1.5px dashed var(--kz-color-line); border-radius: var(--kz-radius-pill); background: transparent; color: var(--kz-color-subtle); font-size: 13.5px; font-weight: 600; cursor: pointer; transition: var(--kz-transition); }
.kz-attach-field .kz-attach-btn:hover { border-color: var(--kz-color-primary); color: var(--kz-color-primary); }
.kz-attach-field .kz-attach-btn svg { width: 16px; height: 16px; flex: 0 0 auto; }
.kz-inbox__composer { border-top: 1px solid var(--kz-color-line); padding: 14px 18px; }
.kz-inbox__thread--empty { align-items: center; justify-content: center; }

@media (max-width: 900px) {
    .kz-inbox { grid-template-columns: 1fr; }
    .kz-inbox__thread:not(.kz-inbox__thread--empty) ~ .kz-inbox__list,
    .kz-inbox__list:has(~ .kz-inbox__thread:not(.kz-inbox__thread--empty)) { display: none; }
}

/* Offers inside a conversation (MakeOffer / negotiation module). The bubble keeps its
   shape; the offer just fills it with an amount, a status and its actions. */
/* An offer is a transactional card, not a chat line: it keeps the neutral surface even on
   the sender's side, so its buttons never land on the primary colour. */
.kz-inbox__bubble--offer,
.kz-inbox__bubble-row.is-mine .kz-inbox__bubble--offer {
    display: block;
    min-width: 190px;
    background: var(--kz-color-soft);
    color: var(--kz-color-ink);
}
.kz-offer { display: flex; flex-direction: column; gap: 8px; }
.kz-offer__amount { font-weight: 700; font-size: 17px; }
.kz-offer__was { margin-left: 8px; font-weight: 500; font-size: 13.5px; text-decoration: line-through; opacity: .7; }
.kz-offer__status { font-size: 13px; }
.kz-offer__status--pending { opacity: .8; }
.kz-offer__status--accepted { color: var(--kz-color-success); font-weight: 600; }
.kz-offer__status--declined { color: var(--kz-color-danger); font-weight: 600; }
.kz-offer__actions { display: flex; flex-wrap: wrap; gap: 8px; }
.kz-btn--sm { padding: 7px 12px; font-size: 13px; }

/* Listing header actions (make an offer / buy) and quote banner. */
.kz-inbox__listing-block { border-bottom: 1px solid var(--kz-color-line); background: var(--kz-color-soft); }
.kz-inbox__listing-block .kz-inbox__listing { border-bottom: 0; }
.kz-inbox__listing-actions { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 18px 14px; }
.kz-inbox__notice {
    margin: 0;
    padding: 12px 18px;
    font-size: 13.5px;
    background: var(--kz-color-warning-tint);
    color: var(--kz-color-ink);
    border-bottom: 1px solid var(--kz-color-line);
}

/* Attachments on an ordinary message. */
.kz-inbox__attachments { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.kz-inbox__attachment-img { width: 88px; height: 88px; border-radius: 10px; object-fit: cover; display: block; }
.kz-inbox__attachment-file, .kz-inbox__attachment-error { font-size: 13px; text-decoration: underline; }

/* Header details the legacy "About" column carried (city, member-since, rating, badges),
   folded into the thread header — the two-pane design has no third column. */
.kz-inbox__thread-who { min-width: 0; flex: 1 1 auto; }
.kz-inbox__since { margin-left: 6px; opacity: .85; }
.kz-inbox__rating { display: block; font-size: 13px; }
.kz-inbox__badges { flex: 0 0 auto; display: flex; gap: 6px; align-items: center; }
.kz-inbox__danger { margin: 10px 0 0; text-align: right; }
.kz-inbox__danger a { font-size: 12.5px; color: var(--kz-color-subtle); text-decoration: underline; }
.kz-inbox__danger a:hover { color: var(--kz-color-danger); }

/* Inbox response time badge ("Répond en ~ 2 h"), rendered by the
   KreezalidApps/Inbox.ResponseTime cell on a seller's profile. Lives in the portable
   layer, not in a theme: the cell is plugin-owned and any portable theme that prints
   it gets the same treatment. */
.kz-response-time { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.kz-response-time__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--kz-color-success, #16a34a); flex: none; }
