/* ==========================================================================
   cineapp · styles.css · single stylesheet
   ==========================================================================

   File layout (one section per concern, jump-to via the banner names):

     [BASE]           palette, body, topbar, page-head, flash, footer
     [LIST_SHOWS]     filter bar, table, sort arrows, mob-sort, DataTables UI
     [DISPLAY_SHOW]   (phase 5)
     [SHOW_GRAPHS]    stats graphs — navigator + Chart.js card
     [ACTIVITY_FLOW]  activity feed — DataTables timeline
     [CHAT]           the Trollbox — real-time SocketIO room
     [LOGIN]          front door — poster medley + login panel
     [...]            (other pages added here)

   Per-user color rules (depending on User.id at render time) are generated
   inline by their template via Jinja loops — they cannot live in this static
   file. Everything else belongs here.

   ========================================================================== */


/* ==========================================================================
   [BASE] — shared by every page (declared in base.html)
   ========================================================================== */

:root {
  --paper:        #f6f3f0;
  --paper-soft:   #ede8e4;
  --paper-card:   #ffffff;
  --ink:          #1c1820;
  --ink-mid:      #5d5460;
  --ink-dim:      #9a8fa1;
  --rule:         #d6cfd8;
  --rule-soft:    #e8e2ec;
  --violet:       #6b3a9e;
  --violet-rgb:   107, 58, 158;   /* channels of --violet, for rgba(var(--violet-rgb), α) */
  --violet-soft:  #a78bca;
  --violet-tint:  rgba(var(--violet-rgb), .06);

  /* Accent palette — colors used for non-user UI roles (favorite badges,
     activity-type tags, legend swatches, etc.). User-specific colors are
     read from User.theme_color at render time, NOT picked from this palette. */
  --accent-violet: #6b3a9e;
  --accent-rose:   #b04860;
  --accent-teal:   #2f8488;
  --accent-sage:   #5e7a48;

  --ok:    #5e7a48;
  --warn:  #b08c00;
  --error: #b04860;

  /* Font stacks — one source of truth for the type system. */
  --font-ui:      'Outfit', sans-serif;            /* labels, eyebrows, buttons */
  --font-display: 'Bricolage Grotesque', sans-serif; /* italic headings, brand */
  --font-body:    'Inter', sans-serif;             /* prose, synopsis, comments */
  --font-mono:    'JetBrains Mono', monospace;     /* stats kickers, byline */

  /* Shared radius for large content cards (small blocks like reviews / region
     releases keep their own tighter radius). */
  --card-radius: 16px;
}

/* ============== [CAPTIONS] — shared uppercase label/eyebrow recipe ==============
   One source for the Outfit-900 .14em uppercase ink-dim caption type used across
   labels, eyebrows, table headers, the footer signature, etc. font-size stays
   per-rule below. */
.foot,
.filter-bar .control-label,
.form-label,
.list thead .cols th,
.dt-info,
.show-nav,
.fav-menu a,
.show-detail .eyebrow,
.reviews-count,
.score-empty,
.mark-studio .mark-score-max,
.wizard-page-indicator,
.wizard-hint,
.wizard-confirm-meta label,
.flux-count,
.graph-nav-label,
.login-field label,
.login-powered-label,
.id-role,
.panel-label,
.profile-field label {
  font-family: var(--font-ui); font-weight: 900;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-dim);
}



* { box-sizing: border-box; margin: 0; padding: 0; }

html, body { background: var(--paper); color: var(--ink); }
body {
  font-family: 'Inter', -apple-system, sans-serif;
  font-size: 15.5px;
  line-height: 1.55;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  --st-accent: #6b3a9e;
  --st-tint: none;
  background-image:
    var(--st-tint, none),
    radial-gradient(1200px 600px at 20% -10%, rgba(var(--violet-rgb),.05), transparent 60%),
    radial-gradient(900px 500px at 90% 10%, rgba(176, 72, 96, .035), transparent 60%);
  /* Make <body> a stacking context so the fixed .show-type-watermark can use
     z-index: -1 to sit behind all normal-flow content while staying above
     the body background. Without this, the watermark (z-index: auto, fixed)
     paints above every normal-flow block on every page. */
  isolation: isolate;
}
body[data-show-type="movie"] {
  --st-accent: #6b3a9e;
  --st-tint: radial-gradient(1100px 700px at 80% 110%, rgba(107, 58, 158, .07), transparent 60%);
}
body[data-show-type="tvshow"] {
  --st-accent: #3a6b9e;
  --st-tint: radial-gradient(1100px 700px at 80% 110%, rgba(58, 107, 158, .07), transparent 60%);
}
body[data-show-type="videogame"] {
  --st-accent: #3a9e6b;
  --st-tint: radial-gradient(1100px 700px at 80% 110%, rgba(58, 158, 107, .07), transparent 60%);
}
::selection { background: var(--violet); color: var(--paper); }

/* ---------- Show-type watermark (fixed bottom-right) ---------- */
.show-type-watermark {
  position: fixed;
  right: -40px;
  bottom: -40px;
  font-size: 440px;
  line-height: 1;
  color: var(--st-accent, var(--ink));
  opacity: .07;
  pointer-events: none;
  transform: rotate(-8deg);
  transform-origin: bottom right;
  /* Sit behind every page content. body has `isolation: isolate`, so this
     z-index is local to body's stacking context: the watermark paints above
     body's background but below all normal-flow blocks. */
  z-index: -1;
}

@media (max-width: 900px) {
  .show-type-watermark { font-size: 280px; right: -28px; bottom: -28px; }
}
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

/* ---------- Top bar ---------- */
.topbar {
  position: sticky; top: 0; z-index: 50;
  background: rgba(246, 243, 240, .92);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--rule);
}
/* Show-type accent (3px) pinned to the bottom of the STICKY topbar so the
   coloured separator stays visible while scrolling. It used to be a separate
   non-sticky .show-type-strip div that scrolled out of view. Shown only when a
   show-type is active (data-show-type non-empty), matching the old condition. */
body:not([data-show-type=""]) .topbar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 3px;
  background: var(--st-accent);
}
.topbar-inner {
  max-width: 1480px;
  margin: 0 auto;
  padding: 18px 56px;
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  gap: 36px;
  align-items: center;
}
.brand {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 22px;
  letter-spacing: -.02em;
  color: var(--ink);
  display: inline-flex; align-items: center; gap: 8px;
}
.brand::before {
  content: ""; width: 26px; height: 26px; flex: none;
  background: url(../images/logos/cineapp-logo.png) center / contain no-repeat;
}

nav.menu { display: flex; gap: 32px; flex-wrap: nowrap; }
nav.menu a {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 18px;
  color: var(--ink-mid);
  letter-spacing: -.01em;
  white-space: nowrap;
  transition: color .2s;
}
nav.menu a:hover, nav.menu a.active { color: var(--violet); }
nav.menu a.active { font-weight: 500; }

/* Mobile hamburger toggle + close button — hidden on desktop */
.menu-toggle, .menu-close { display: none; }

/* ---------- Search pill (top bar) ---------- */
.search-pill {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 10px 18px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 999px;
  font-size: 14px;
  color: var(--ink-mid);
  min-width: 280px;
  transition: border-color .2s;
}
.search-pill:hover, .search-pill:focus-within { border-color: var(--violet); }
.search-pill input {
  flex: 1; border: 0; background: transparent; outline: none;
  font: inherit; color: var(--ink);
}
.search-pill input::placeholder { color: var(--ink-dim); }
.search-pill button.search-submit {
  background: transparent; border: 0; color: var(--ink-dim);
  padding: 0 4px; cursor: pointer;
}
.search-pill button.search-submit:hover { color: var(--violet); }

/* ---------- User chip + dropdown ---------- */
.user-chip {
  position: relative;
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 14px; color: var(--ink);
  cursor: pointer;
}
/* Avatar circle — shared recipe (initials fallback in Bricolage italic on a
   round, clipped chip). Per-instance rules below carry only size/background. */
.user-chip .ava,
.list thead .users th .ava,
.avatar-large,
.subcomment .ava,
.composer .ava,
.msg-avatar,
.id-ava {
  position: relative;
  border-radius: 50%;
  overflow: hidden;
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-style: italic; font-weight: 600;
  color: #fefdfa;
}
.user-chip .ava {
  width: 32px; height: 32px; font-size: 14px;
  background: linear-gradient(135deg, var(--violet-soft), var(--violet));
}
/* Avatar photo overlay — shared by every avatar circle (initials show through
   the gradient/initials fallback until the <img> loads, then it covers them). */
.user-chip .ava img,
.list thead .users th .ava img,
.avatar-large img,
.subcomment .ava img,
.composer .ava img,
.msg-avatar img,
.id-ava img,
.dash .latest-meta .rater .ava img,
.dash .you-block .ava-big img,
.dash .others .row .ava img,
.act-av img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.user-chip #topbar-username { font-weight: 500; }
.user-chip .caret { font-size: 10px; opacity: .55; margin-left: 2px; }

.user-menu {
  position: absolute; top: calc(100% + 10px); right: 0;
  min-width: 220px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(28, 24, 32, .12), 0 2px 6px rgba(28, 24, 32, .06);
  padding: 6px;
  opacity: 0; pointer-events: none;
  transform: translateY(-4px);
  transition: opacity .15s, transform .15s;
  z-index: 100;
}
.user-chip.is-open .user-menu,
.user-menu:hover {
  opacity: 1; pointer-events: auto; transform: translateY(0);
}
.user-menu a {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 8px;
  font-family: var(--font-body); font-size: 14px;
  color: var(--ink-mid);
  transition: background .15s, color .15s;
}
.user-menu a:hover { background: var(--paper-soft); color: var(--ink); }
.user-menu a.active { color: var(--violet); }
.user-menu .sep {
  height: 1px; background: var(--rule-soft); margin: 4px 8px;
}
.user-menu a i { width: 16px; text-align: center; color: var(--ink-dim); }

/* ---------- Page head (centered H1) ---------- */
.page-head {
  max-width: 1480px; margin: 0 auto;
  padding: 24px 56px 36px;
  text-align: center;
}
.page-head h1 {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500;
  font-size: clamp(40px, 4.5vw, 64px);
  line-height: .98; letter-spacing: -.035em; color: var(--ink);
}
.page-head h1 em { font-style: italic; font-weight: 500; color: var(--violet); }
.page-head .stats {
  display: flex; gap: 32px;
  font-family: var(--font-mono); font-size: 12px;
  color: var(--ink-mid); letter-spacing: .04em;
}
.page-head .stats .num {
  font-family: var(--font-ui); font-weight: 600;
  font-size: 32px; color: var(--violet);
  letter-spacing: -.01em; display: block; line-height: 1;
  margin-bottom: 4px; font-feature-settings: "tnum" 1;
}
.page-head .stats .lbl { text-transform: uppercase; }

/* ---------- Flash messages ---------- */
.flash-stack {
  position: fixed; top: 16px; left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  display: flex; flex-direction: column; gap: 10px;
  pointer-events: none;
  width: min(560px, 92vw);
}
.flash {
  pointer-events: auto;
  position: relative;
  padding: 12px 44px 12px 18px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--ink-mid);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(28, 24, 32, .12), 0 2px 6px rgba(28, 24, 32, .06);
  font-family: var(--font-body); font-size: 14px;
  color: var(--ink);
}
.flash.success { border-left-color: var(--ok); }
.flash.warning { border-left-color: var(--warn); }
.flash.danger,
.flash.error   { border-left-color: var(--error); }
.flash .close {
  position: absolute; top: 8px; right: 10px;
  background: transparent; border: 0; padding: 4px 8px;
  font-size: 18px; line-height: 1; color: var(--ink-dim);
  cursor: pointer; border-radius: 6px;
  transition: background .15s, color .15s;
}
.flash .close:hover { background: var(--paper-soft); color: var(--ink); }

/* ---------- Poster placeholder (shared) ----------
   Used by display_show, list_shows row, dashboard suggestion. Always rendered
   absolutely positioned inside a `position: relative` container ; the real
   `<img>` sits on top of it. When the image fails to load (onerror →
   display:none / remove), the placeholder remains visible. The variant
   classes .lg / .sm tune the icon size. */
.poster-placeholder {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background:
    radial-gradient(circle at 30% 30%, rgba(var(--violet-rgb),.35), transparent 60%),
    var(--ink);
  color: var(--violet);
}
.poster-placeholder i {
  font-size: 64px;
  opacity: .35;
  transform: rotate(-8deg);
  transition: transform .4s ease, opacity .4s ease;
}
.poster-placeholder.lg i { font-size: 130px; }
.poster-placeholder.sm i { font-size: 22px; }
.dash .cata-suggestion:hover .poster-placeholder i {
  transform: rotate(-8deg) scale(1.06);
  opacity: .5;
}

/* ---------- Footer ---------- */
.foot {
  border-top: 1px solid var(--rule);
  padding: 24px 56px;
  max-width: 1480px;
  margin: 32px auto 0;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  font-size: 11px;
}

/* ==========================================================================
   Responsive — ≤920px
   ========================================================================== */
@media (max-width: 920px) {
  html, body { overflow-x: hidden; }

  .topbar-inner {
    grid-template-columns: auto 1fr auto;
    padding: 14px 20px; gap: 12px;
  }

  /* Hamburger button — placed where the desktop nav used to be */
  .menu-toggle {
    display: inline-flex; flex-direction: column;
    justify-content: center; align-items: center;
    width: 38px; height: 38px;
    background: transparent; border: 1px solid var(--rule);
    border-radius: 999px;
    cursor: pointer;
    gap: 4px;
    transition: border-color .2s;
    grid-column: 2; justify-self: end;
  }
  .menu-toggle:hover { border-color: var(--violet); }
  .menu-toggle-bar {
    display: block; width: 16px; height: 2px;
    background: var(--ink); border-radius: 1px;
    transition: transform .2s, opacity .2s;
  }

  /* Full-screen overlay menu */
  nav.menu {
    position: fixed; top: 0; left: 0;
    width: 100vw; height: 100vh; height: 100dvh;
    z-index: 200;
    display: none;
    flex-direction: column;
    justify-content: center; align-items: center;
    gap: 22px;
    background: var(--paper);
    padding: 80px 24px;
    overflow-y: auto;
  }
  nav.menu.is-open { display: flex; animation: menuFade .25s ease both; }
  @keyframes menuFade {
    from { opacity: 0; transform: translateY(-8px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  nav.menu a {
    font-family: var(--font-display);
    font-style: italic; font-weight: 500;
    font-size: 32px; line-height: 1;
    letter-spacing: -.02em;
    color: var(--ink);
    padding: 8px 4px;
  }
  nav.menu a.active {
    color: var(--violet); font-weight: 600;
  }
  nav.menu a.active::before {
    content: "▸ "; color: var(--violet); font-style: normal;
  }

  /* Close button (× in top right of the overlay) */
  .menu-close {
    display: grid; place-items: center;
    position: absolute; top: 18px; right: 20px;
    width: 38px; height: 38px;
    background: transparent; border: 1px solid var(--rule); border-radius: 50%;
    font-family: var(--font-display);
    font-weight: 500; font-size: 22px; line-height: 1;
    color: var(--ink); cursor: pointer;
    transition: border-color .2s, color .2s;
  }
  .menu-close:hover { border-color: var(--violet); color: var(--violet); }

  .search-pill { min-width: 0; width: 100%; padding: 8px 14px; font-size: 13px; grid-column: 1 / -1; order: 3; }
  .user-chip #topbar-username, .user-chip .caret { display: none; }

  .page-head, .foot {
    padding-left: 20px; padding-right: 20px;
  }
  .page-head { padding-top: 18px; padding-bottom: 24px; }
}

/* Below ~600px the two footer lines no longer fit side by side and the base
   space-between row wraps them into a ragged left-aligned block. Stack them as
   a single centred column instead. */
@media (max-width: 600px) {
  .foot {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
  }
}

@media (max-width: 480px) {
  .page-head h1 { font-size: 36px; }
}


/* ==========================================================================
   [LIST_SHOWS] — movie / tvshow / videogame list (list_shows.html)
   ==========================================================================
   Note: per-user color rules (.user-<id> backgrounds and accents) are
   generated by Jinja in the template — see the inline <style> block.
   ========================================================================== */



/* ----- filter strip ----- */
.filter-strip { max-width: 1480px; margin: 0 auto; padding: 0 56px 28px; }
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 18px 20px; background: var(--paper-card);
  border: 1px solid var(--rule); border-radius: var(--card-radius);
  box-shadow: 0 1px 0 var(--rule-soft);
}
.filter-bar .form-group { margin: 0; display: inline-flex; align-items: center; gap: 8px; }
.filter-bar .control-label {
  font-size: 10.5px;
  margin: 0;
  white-space: nowrap;
}
.filter-bar select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 8px 32px 8px 14px;
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--ink); cursor: pointer;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%239a8fa1' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 9px;
  transition: border-color .15s, color .15s;
}
.filter-bar select:hover, .filter-bar select:focus {
  border-color: var(--violet); color: var(--violet); outline: none;
}
/* Filter-bar action group — shared by every .filter-bar (movie list, homework
   list, …). The submit / reset buttons are wrapped in .filter-actions so they
   stay grouped and pushed to the right on one row; when the bar wraps at
   intermediate widths they drop to their own centred line (see the
   921–1280px band) instead of being stranded next to a half-empty row. */
.filter-bar .filter-actions {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 10px;
}
/* ============== [BUTTONS] — one .btn pill ; modifiers carry colour & size ==============
   Used everywhere via class="btn", "btn btn-solid", "btn btn-ghost", etc. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 11px 20px;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--paper-card); color: var(--ink-mid);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; line-height: 1.55; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer; transition: all .2s; text-decoration: none;
  -webkit-appearance: none; appearance: none;
}
.btn:hover { border-color: var(--violet); color: var(--violet); }
/* solid — canonical primary CTA. Ink gradient with violet glow on top-left
   and a tinted drop shadow ; lifts on hover. Used everywhere a primary
   action is offered (Filtrer, Noter, Continuer, Publier…). */
.btn-solid {
  background:
    radial-gradient(120% 80% at 25% 10%, rgba(var(--violet-rgb),.55), transparent 60%),
    var(--ink);
  border-color: var(--ink);
  color: var(--paper-card);
  box-shadow:
    0 12px 28px -12px rgba(var(--violet-rgb),.55),
    0 4px 12px -6px rgba(28, 24, 32, .35),
    inset 0 1px 0 rgba(255, 255, 255, .08);
  transition: transform .2s, box-shadow .2s, background .2s, border-color .2s, color .2s;
}
.btn-solid:hover {
  transform: translateY(-2px);
  background:
    radial-gradient(120% 80% at 25% 10%, rgba(var(--violet-rgb),.75), transparent 60%),
    var(--ink);
  border-color: var(--ink);
  color: var(--paper-card);
  box-shadow:
    0 18px 38px -14px rgba(var(--violet-rgb),.75),
    0 4px 12px -6px rgba(28, 24, 32, .35),
    inset 0 1px 0 rgba(255, 255, 255, .12);
}
/* ghost — transparent surface */
.btn-ghost { background: transparent; }
/* compact — table-row scale (Noter / Devoir). Tuned so the natural
   width fits inside the .cell-mark content area (≈65 px at 7 % column
   width), letting the button stay perfectly centred without overflowing
   into the next column. */
.btn-rate, .btn-homework {
  gap: 3px; padding: 4px 8px;
  font-size: 9.5px; letter-spacing: .05em;
}
/* rate — Noter pill renders with `btn btn-solid btn-rate`, so it inherits
   the canonical primary CTA look (ink+violet gradient, lifted hover) from
   .btn-solid. .btn-rate only contributes the table-row compact sizing
   above (shared with .btn-homework). */
/* homework — gold tint, pencil glyph, non-actionable.
   No hover feedback whatsoever: cursor stays default (overrides the
   pointer from .btn) and :hover re-states base colours to neutralise the
   global .btn:hover violet effect. */
.btn-homework {
  background: rgba(255, 200, 0, .12); border-color: #d49a00; color: #8a6500;
  cursor: default;
}
.btn-homework:hover { background: rgba(255, 200, 0, .12); border-color: #d49a00; color: #8a6500; }
.btn-homework::before { content: "✎"; font-size: 10px; }

/* ============== [FORMS] — standard field component of the charte ==============
   The plain input/select/label. Distinctive form UIs (filter-pill selects,
   wizard selects, login, search box, notif switches, radio cards) keep their
   own bespoke styling via more specific selectors. */
.form-control, .form-select {
  width: 100%;
  padding: 10px 13px;
  background-color: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 10px;
  font-family: var(--font-body); font-size: 15px;
  color: var(--ink); outline: none;
  transition: border-color .2s, box-shadow .2s;
}
.form-control:focus, .form-select:focus {
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(var(--violet-rgb), .12);
}
.form-label {
  font-size: 11px;
}

/* ============== [CARDS] — shared card surface ==============
   paper-card + rule hairline border + soft shadow. Column/block cards wear
   class="card" ; layout-specific cards (filter row, homework grid, result
   tiles, DataTables table) keep their own styling. */
.card {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--card-radius);
  box-shadow: 0 1px 0 var(--rule-soft);
}

/* ----- mobile sort bar (visible only ≤920px) ----- */
.mob-sort { display: none; }

/* ----- list wrap ----- */
.list-wrap { max-width: 1480px; margin: 0 auto; padding: 0 56px 24px; }

/* ----- table card (applied to the DT layout row that holds the table only) ----- */
.dt-layout-row.dt-layout-table {
  background: var(--paper-card); border: 1px solid var(--rule);
  border-radius: var(--card-radius);
  /* `visible` (not `hidden`) so the hover bubble on .cell-mark-val can
     extend past the top / bottom edges of the table card without being
     clipped. The wrapper's own background still respects its border-radius. */
  overflow: visible;
  box-shadow: 0 1px 0 var(--rule-soft);
  margin: 0 0 14px;
}
.dt-layout-row.dt-layout-table .dt-layout-cell { padding: 0 !important; }
.dt-layout-row.dt-layout-table { margin-left: 0; margin-right: 0; }
/* `separate` (not `collapse`) so the header's corner cells can carry a
   border-radius: the table card uses `overflow: visible` (for the hover bubble),
   so the grey header background must round its own top corners to follow the
   card. border-spacing: 0 keeps the per-cell borders touching, identical to
   collapse visually. */
.list { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 14.5px; table-layout: fixed; }

/* Column widths (px) for the fixed layout — set on DataTables' generated
   <colgroup> so no width is passed in JS, and so the sticky-header mirror (which
   clones that colgroup, see list_shows.html) inherits them for free. Pattern:
   Titre 400 · Moy 88, then a Fav 52 · Note 95 · Date 73 trio per user. These
   must match the frozen sticky offsets and --list-min-width. */
.list-wrap .list > colgroup > col:nth-child(3n)   { width: 52px; }  /* Fav  */
.list-wrap .list > colgroup > col:nth-child(3n+1) { width: 95px; }  /* Note */
.list-wrap .list > colgroup > col:nth-child(3n+2) { width: 73px; }  /* Date */
.list-wrap .list > colgroup > col:nth-child(1)    { width: 400px; } /* Titre (overrides 3n+1) */
.list-wrap .list > colgroup > col:nth-child(2)    { width: 88px; }  /* Moy   (overrides 3n+2) */

/* Per-user colour. The value (User.theme_color) is bound to --u per user id in
   the list_shows.html runtime <style>; the rules below only read that variable,
   so a single static rule set covers every user instead of one block per id. */
.list thead .users th .ava,
.mob-sort-row .ava                { background: var(--u); }
.cell-mark-val,
.list thead .users th .uname,
.list tbody td.cell-fav::before,
.mob-sort-row .mob-sort-rowlabel  { color: var(--u); }

/* user-grouped header (top row) */
.list thead .users th {
  padding: 18px 8px 14px;
  background: var(--paper-soft);
  border-bottom: 1px dashed var(--rule);
  vertical-align: bottom;
  text-align: center;
}
/* Round the header's outer top corners to match the table card (which can't use
   overflow:hidden — it needs visible for the hover bubble). -1px = the card's
   border width, so the fill sits just inside the rounded border. */
.list thead .users th:first-child { border-top-left-radius: calc(var(--card-radius) - 1px); }
.list thead .users th:last-child  { border-top-right-radius: calc(var(--card-radius) - 1px); }
.list thead .users th .ulabel {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.list thead .users th .ava {
  width: 38px; height: 38px; font-size: 16px;
  border: 2px solid var(--paper-card);
  box-shadow: 0 0 0 1px currentColor;
}
.list thead .users th .uname {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10.5px; letter-spacing: .16em;
  text-transform: uppercase;
}

/* sub header (column names) */
.list thead .cols th {
  padding: 12px 6px 14px;
  font-size: 10.5px;
  border-bottom: 1px solid var(--rule);
  background: var(--paper-card);
  white-space: nowrap;
  text-align: center;
}
/* "TITRE" header is centered; the cell content (poster + name + director)
   stays left-aligned. !important is needed because DataTables loads after
   styles.css and applies .dt-center to every td. */
.list thead .cols th:first-child { text-align: center; padding: 12px 8px 14px; }
.list tbody td.t-title { text-align: left !important; }
.list tbody td.t-title .cell-title { justify-content: flex-start; }
/* Sort indicators — DataTables 2.x adds .dt-orderable-* / .dt-ordering-* automatically. */
.list thead .cols th.dt-orderable-asc,
.list thead .cols th.dt-orderable-desc { cursor: pointer; transition: color .2s; }
.list thead .cols th.dt-orderable-asc:hover,
.list thead .cols th.dt-orderable-desc:hover { color: var(--violet); }
.list thead .cols th .sort {
  display: inline-flex; flex-direction: column;
  vertical-align: middle; margin-left: 4px;
  line-height: .8; position: relative; top: -1px;
}
.list thead .cols th .sort i {
  font-style: normal; font-size: 8px;
  color: var(--ink-dim); opacity: .45;
  transition: opacity .15s, color .15s;
}
.list thead .cols th.dt-orderable-asc:hover .sort i,
.list thead .cols th.dt-orderable-desc:hover .sort i { opacity: .7; }
.list thead .cols th.dt-ordering-asc { color: var(--violet); }
.list thead .cols th.dt-ordering-asc .sort .up { opacity: 1; color: var(--violet); }
.list thead .cols th.dt-ordering-asc .sort .dn { opacity: .2; }
.list thead .cols th.dt-ordering-desc { color: var(--violet); }
.list thead .cols th.dt-ordering-desc .sort .dn { opacity: 1; color: var(--violet); }
.list thead .cols th.dt-ordering-desc .sort .up { opacity: .2; }
.list thead .cols th.my-col { color: var(--violet); }
/* The "Titre" column header stays neutral when sorted — the violet sort cue
   is reserved for numerical / user-data columns. */
.list thead .cols th:first-child.dt-ordering-asc,
.list thead .cols th:first-child.dt-ordering-desc { color: var(--ink-dim); }
.list thead .cols th:first-child.dt-ordering-asc .sort .up,
.list thead .cols th:first-child.dt-ordering-desc .sort .dn { color: var(--ink-dim); }
/* Hide DataTables' native sort indicator (we use our own .sort spans) */
.list thead .cols th .dt-column-order { display: none !important; }

/* body */
.list tbody td {
  padding: 14px 6px; vertical-align: middle;
  border-bottom: 1px solid var(--rule-soft);
  text-align: center;
}
.list tbody td:first-child { text-align: left; padding: 14px 8px; }
.list tbody td.cell-when { padding-right: 10px; }
.list tbody td.cell-fav { padding-left: 10px; }
.list tbody tr { transition: background .15s; }
.list tbody tr:hover { background: var(--paper-soft); }
/* border-bottom: 0 !important to beat DataTables' `.dt-container.dt-empty-footer
   tbody > tr:last-child > *` (border rgba(0,0,0,.3), loaded after styles.css).
   Applies to desktop AND the mobile card's data cells. The last card's
   title/moyenne dividers are re-added in the mobile block below. */
.list tbody tr:last-child td { border-bottom: 0 !important; }

/* visual separator between user trios */
.list tbody td.cell-fav { border-left: 1px solid var(--rule-soft); }
.list tbody td.cell-fav.user-mine { border-left: 1px solid var(--rule); }

/* title cell */
.cell-title { display: flex; align-items: center; gap: 14px; }
.cell-title .poster {
  width: 38px; height: 56px;
  background: var(--paper-soft);
  border: 1px solid var(--rule);
  border-radius: 4px;
  flex-shrink: 0; overflow: hidden;
  position: relative;
  transform: none; transition: none;
}
.cell-title .poster:hover { transform: none; }
.cell-title .poster img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.cell-title .ttx { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.cell-title .tname {
  font-family: var(--font-display);
  font-weight: 500; font-size: 16px;
  letter-spacing: -.015em; color: var(--ink);
  transition: color .15s;
}
.cell-title .tname:hover { color: var(--violet); }
.cell-title .tdir {
  font-size: 12.5px; color: var(--ink-dim); font-style: italic;
}

/* moyenne cell */
.cell-avg {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 26px;
  color: var(--ink); letter-spacing: -.02em;
  line-height: 1; font-feature-settings: "tnum" 1;
}
.cell-avg.empty {
  font-family: var(--font-body);
  color: var(--ink-dim); font-size: 14px;
  font-style: italic; font-weight: 400;
}

/* fav star */
.cell-fav-icon {
  display: inline-block;
  font-size: 26px; line-height: 1;
  transition: transform .2s, color .2s;
}
.cell-fav-icon.off { color: var(--rule); }
tr:hover .cell-fav-icon.on { transform: scale(1.1); }

/* Per-star-type colors — palette adaptation of legacy BS3 colors.
   The favorite type is carried by [data-star-type="..."] on every ★-bearing
   element (list cells .cell-fav-icon.on, review meta .review-favstar,
   favorite button .fav-btn). Adding a new favorite type to the DB only
   requires adding one color rule here. */
.cell-fav-icon.on[data-star-type="favorite_star"],
.review-favstar[data-star-type="favorite_star"],
.fav-btn[data-star-type="favorite_star"] { color: var(--warn); }

.cell-fav-icon.on[data-star-type="homework_star"],
.review-favstar[data-star-type="homework_star"],
.fav-btn[data-star-type="homework_star"] { color: var(--accent-rose); }

.cell-fav-icon.on[data-star-type="mustsee_star"],
.review-favstar[data-star-type="mustsee_star"],
.fav-btn[data-star-type="mustsee_star"] { color: var(--violet); }

.cell-fav-icon.on[data-star-type="random_homework_star"],
.review-favstar[data-star-type="random_homework_star"],
.fav-btn[data-star-type="random_homework_star"] { color: var(--accent-teal); }

/* mark cell */
.cell-mark, .cell-when { text-align: center; }
.cell-mark .cell-mark-val,
.cell-when .cell-when-val,
.cell-mark .btn-rate,
.cell-mark .btn-homework {
  display: inline-flex; justify-content: center;
}
.cell-mark-val {
  font-family: var(--font-ui);
  font-weight: 600; font-size: 19px;
  letter-spacing: -.005em; line-height: 1;
  font-feature-settings: "tnum" 1;
  position: relative;
}
.cell-mark-val .empty,
.cell-when-val .empty {
  color: var(--ink-dim);
  font-family: var(--font-body);
  font-size: 14px; font-style: italic; font-weight: 400;
}
.cell-mark-val.has-comment { cursor: help; }

/* hover bubble for comments (BS3 popover preserved).
   position: fixed (not absolute) + JS-set top/left (see list_shows.html) so the
   bubble is never clipped by an ancestor's overflow — notably the table card,
   which becomes a horizontal scroller in the 921–1280px band. */
.cell-mark .cell-mark-val .bubble {
  position: fixed;
  top: 0; left: 0;
  transform: translate(-50%, 6px);
  width: 240px; pointer-events: none;
  opacity: 0; transition: opacity .15s, transform .15s;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(28, 24, 32, .12), 0 2px 6px rgba(28, 24, 32, .06);
  text-align: left; z-index: 10;
  overflow: hidden;
}
.cell-mark .cell-mark-val:hover .bubble {
  opacity: 1; transform: translate(-50%, 0);
}
.cell-mark .cell-mark-val .bubble .b-title {
  display: block; padding: 9px 14px 7px;
  background: var(--ink); color: var(--paper);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase;
}
.cell-mark .cell-mark-val .bubble .b-body {
  display: block; padding: 10px 14px 12px;
  font-family: var(--font-body); font-weight: 400;
  font-size: 13px; line-height: 1.45;
  color: var(--ink); letter-spacing: 0;
}
.cell-mark .cell-mark-val .bubble::after {
  content: ""; position: absolute;
  bottom: -6px; left: 50%; transform: translateX(-50%) rotate(45deg);
  width: 10px; height: 10px;
  background: var(--paper-card);
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
/* Flipped variant — applied by JS when the default above-placement would
   clip past the top of the viewport. Bubble sits BELOW the cell with the
   arrow on top pointing up. JS sets the actual top; here we only flip the
   entry-animation direction and (below) the arrow. */
.cell-mark .cell-mark-val .bubble.bubble-below {
  transform: translate(-50%, -6px);
}
.cell-mark .cell-mark-val:hover .bubble.bubble-below {
  transform: translate(-50%, 0);
}
.cell-mark .cell-mark-val .bubble.bubble-below::after {
  bottom: auto; top: -6px;
  border-right: 0; border-bottom: 0;
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}

/* when cell */
.cell-when-val {
  font-family: var(--font-ui);
  font-weight: 500; font-size: 14px;
  color: var(--ink-mid); letter-spacing: .005em;
  font-feature-settings: "tnum" 1;
}
.cell-when-val.cinema { color: var(--accent-rose); font-weight: 600; }


/* DataTables 2.x — length menu + info + paging */
#showslist_wrapper { font-family: var(--font-body); }

/* Top row (length menu) and bottom row (info + paging) sit OUTSIDE the table card */
#showslist_wrapper > .dt-layout-row:not(.dt-layout-table) {
  padding: 12px 4px;
  margin: 0;
}
/* Top info row gets the same separator treatment as the activity feed's
   .flux-head: a hairline rule with breathing room before the table, which
   otherwise sat glued (~12px, no rule) under the info line. */
#showslist_wrapper > .dt-layout-row:not(.dt-layout-table):first-child {
  padding-top: 0; padding-bottom: 16px;
  margin-bottom: 28px;
  border-bottom: 1px solid var(--rule);
}
/* Cancel DataTables' default 0.75em top/bottom margin on the table card so the
   gaps above and below are exact (the CDN rule outweighs our .dt-layout-table
   margin). */
#showslist_wrapper > .dt-layout-row.dt-layout-table { margin-top: 0; margin-bottom: 0; }
/* Bottom info + paging row gets the same separator treatment as the activity
   feed's pager (border-top rule + breathing room), instead of sitting glued
   ~12px under the table. */
#showslist_wrapper > .dt-layout-row:not(.dt-layout-table):last-child {
  padding-top: 18px; padding-bottom: 0;
  margin-top: 18px;
  border-top: 1px solid var(--rule);
}

/* Afficher [100] films par page */
.dt-length { color: var(--ink-dim); }
.dt-length,
.dt-length label,
.dt-length select {
  font-family: var(--font-ui) !important;
  font-weight: 900 !important;
  font-size: 11px !important;
  letter-spacing: .14em !important;
  text-transform: uppercase !important;
}
.dt-length label { display: inline-flex; align-items: center; gap: 8px; margin: 0; color: var(--ink-dim); }
.dt-length select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background: var(--paper-card); border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 6px 32px 6px 18px;
  min-width: 86px;
  font-family: var(--font-ui) !important;
  font-weight: 900 !important;
  font-size: 11px !important;
  letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink); cursor: pointer;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%239a8fa1' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 9px;
  transition: border-color .15s, color .15s;
}
.dt-length select:hover, .dt-length select:focus {
  border-color: var(--violet); color: var(--violet); outline: none;
}

/* Page X sur Y · N œuvres au total */
.dt-info {
  font-size: 11px;
}
/* Keep the DataTables UI text muted — beat vanilla DT's
   `div.dt-container .dt-info/.dt-length {color:inherit}` (doubled-class bump). */
.dt-container.dt-container .dt-info,
.dt-container.dt-container .dt-length,
.dt-container.dt-container .dt-length label { color: var(--ink-dim); }

/* Pagination — vanilla DataTables 2.x markup (<nav> + .dt-paging-button).
   The doubled .dt-container class bumps specificity above DataTables' own
   `div.dt-container .dt-paging .dt-paging-button` rules (which use !important). */
.dt-paging { font-family: var(--font-ui); }
.dt-paging nav { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.dt-container.dt-container .dt-paging .dt-paging-button {
  display: inline-grid; place-items: center;
  min-width: 32px; height: 32px; padding: 0 12px; margin: 0;
  border-radius: 999px !important;
  border: 1px solid transparent !important;
  background: transparent !important;
  color: var(--ink-mid) !important;
  font-family: var(--font-mono);
  font-size: 12px; letter-spacing: 0;
  text-transform: none; font-weight: 500;
  cursor: pointer; transition: all .15s;
}
/* "Précédent" / "Suivant" / first / last — text variants */
.dt-container.dt-container .dt-paging .dt-paging-button.first,
.dt-container.dt-container .dt-paging .dt-paging-button.last,
.dt-container.dt-container .dt-paging .dt-paging-button.previous,
.dt-container.dt-container .dt-paging .dt-paging-button.next {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase;
  padding: 0 16px;
}
.dt-container.dt-container .dt-paging .dt-paging-button:hover {
  color: var(--violet) !important;
  border-color: var(--violet) !important;
}
.dt-container.dt-container .dt-paging .dt-paging-button.current,
.dt-container.dt-container .dt-paging .dt-paging-button.current:hover {
  background: var(--violet) !important;
  border-color: var(--violet) !important;
  color: var(--paper-card) !important;
}
.dt-container.dt-container .dt-paging .dt-paging-button.disabled,
.dt-container.dt-container .dt-paging .dt-paging-button.disabled:hover {
  opacity: .35; cursor: not-allowed;
  border-color: transparent !important;
  color: var(--ink-dim) !important;
  background: transparent !important;
}

/* Processing indicator (DataTables shows it during AJAX) */
.dt-processing {
  background: var(--paper-card) !important;
  border: 1px solid var(--rule) !important;
  border-radius: 10px !important;
  font-family: var(--font-ui) !important;
  font-weight: 900 !important; font-size: 11px !important;
  letter-spacing: .14em !important; text-transform: uppercase !important;
  color: var(--ink-mid) !important;
  box-shadow: 0 4px 12px rgba(28, 24, 32, .12) !important;
}

/* Horizontal scroll for the ratings table on every desktop width (≥921px).
   The table keeps its intrinsic width (--list-min-width: the two fixed columns
   + one 236px trio per user), so all users stay present in the DOM; the table
   card is the horizontal scroller and the left panel — Titre, Moyenne and (when
   you're not a guest) your own Fav/Note/Date — stays frozen so your reference
   never scrolls out of view. .list-wrap's max-width (1480px) bounds the visible
   window to roughly you + 3 other users; the rest are reached by scrolling
   right. The sticky offsets below are the cumulative column widths (must match
   the px `width` options in list_shows.html):
     Titre 400 · Moy 88 · Fav 52 · Note 95 · Date 73  → frozen edge at 708px. */
@media (min-width: 921px) {
  .list { min-width: var(--list-min-width, 1100px); }
  /* the table card is the horizontal scroll container (the hover bubble is
     position:fixed, so it is not clipped by this overflow — see list_shows.html).
     Its native scrollbar is hidden: it sits at the very bottom of a 100-row table
     (unreachable) and is superseded by the sticky top bar; horizontal scrolling
     still works via trackpad / shift-wheel and the mirror bar's own scrollbar. */
  .dt-layout-row.dt-layout-table { overflow-x: auto; scrollbar-width: none; }
  .dt-layout-row.dt-layout-table::-webkit-scrollbar { display: none; }

  /* frozen left panel — Titre + Moyenne always; your own trio only when the
     wrap carries .has-me (i.e. a logged-in, non-guest user). */
  .list thead .users th:nth-child(1),
  .list thead .cols th:nth-child(1),
  .list tbody td:nth-child(1) { position: sticky; left: 0; z-index: 4; }
  .list thead .users th:nth-child(2),
  .list thead .cols th:nth-child(2),
  .list tbody td:nth-child(2) { position: sticky; left: 400px; z-index: 4; }
  .list-wrap.has-me .list thead .users th:nth-child(3),
  .list-wrap.has-me .list thead .cols th:nth-child(3),
  .list-wrap.has-me .list tbody td:nth-child(3) { position: sticky; left: 488px; z-index: 4; }
  .list-wrap.has-me .list thead .cols th:nth-child(4),
  .list-wrap.has-me .list tbody td:nth-child(4) { position: sticky; left: 540px; z-index: 4; }
  .list-wrap.has-me .list thead .cols th:nth-child(5),
  .list-wrap.has-me .list tbody td:nth-child(5) { position: sticky; left: 635px; z-index: 4; }

  /* Lift the current user's rating cell (frozen, z-index 4) above the sticky
     header (z 20) while hovering, so its comment bubble — trapped in the cell's
     sticky stacking context — paints on top instead of being clipped behind the
     header. Pure :hover, in sync with the bubble's own :hover visibility. */
  .list-wrap.has-me .list tbody td:nth-child(4):hover { z-index: 60; }

  /* opaque fills so the scrolling columns don't show through Titre / Moyenne
     (their header cells already have an opaque fill; body cells don't — the
     personal trio's opaque fill comes from the tint rule in list_shows.html). */
  .list tbody td:nth-child(1),
  .list tbody td:nth-child(2) { background: var(--paper-card); }
  .list tbody tr:hover td:nth-child(1),
  .list tbody tr:hover td:nth-child(2) { background: var(--paper-soft); }

  /* right hairline at the frozen/scrolling boundary — on the last frozen
     column: your Date (.has-me) or Moyenne otherwise (guest). */
  .list-wrap.has-me .list thead .users th:nth-child(3)::after,
  .list-wrap.has-me .list thead .cols th:nth-child(5)::after,
  .list-wrap.has-me .list tbody td:nth-child(5)::after,
  .list-wrap:not(.has-me) .list thead .users th:nth-child(2)::after,
  .list-wrap:not(.has-me) .list thead .cols th:nth-child(2)::after,
  .list-wrap:not(.has-me) .list tbody td:nth-child(2)::after {
    content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 1px;
    background: var(--rule);
  }
}

/* Sticky header mirror (desktop ≥921px). A real position:sticky header is
   impossible here: the table card is an overflow-x scroller, so `top` resolves
   against the card, which scrolls away with the page. Instead we mirror the
   header into a bar placed just above the table body (the real <thead> is hidden
   below) — it carries the column header AND its own horizontal scrollbar. The bar
   is always visible so the scrollbar is reachable from the top of the page, and
   it sticks under the site top bar on scroll. Built + kept in sync in
   list_shows.html. */
.list-stickyhead {
  position: sticky; top: 0; z-index: 20;
  display: block; overflow-x: auto; overflow-y: hidden;
  background: var(--paper-card);
  border: 1px solid var(--rule); border-bottom: 0;
  border-radius: var(--card-radius) var(--card-radius) 0 0;
}
.list-stickyhead-table { margin: 0; box-shadow: none; }
/* the mirror replaces the real header on desktop; the card sits flush below it */
@media (min-width: 921px) {
  #showslist > thead { display: none; }
  .dt-layout-row.dt-layout-table {
    border-top: 0; border-top-left-radius: 0; border-top-right-radius: 0;
  }
  /* Empty state — the table is wider than the viewport (frozen columns + one
     trio of columns per user) and scrolls horizontally, so a plain full-span
     colspan cell centres the message on the whole (wide) table, off to the
     right of the visible area. Size the cell to the visible scroll width via a
     container query on the scroll cell and pin it to the viewport so the
     message stays centred as the header/columns scroll. */
  .dt-layout-cell.dt-layout-full { container-type: inline-size; }
  .list tbody td.dt-empty {
    position: sticky; left: 0;
    display: block; width: 100cqw;
    box-sizing: border-box;
    text-align: center;
  }
}
@media (max-width: 920px) { .list-stickyhead { display: none !important; } }

/* Narrow-desktop band (921–1280px) cosmetics: tighter side padding for the
   scroller, and a centred filter bar whose selects no longer fit on one row
   with the actions (the action group wraps onto its own full-width line). */
@media (max-width: 1280px) and (min-width: 921px) {
  .list-wrap { padding: 0 24px 24px; }
  .filter-bar { justify-content: center; }
  .filter-bar .filter-actions { flex-basis: 100%; justify-content: center; }
}

/* ============== RESPONSIVE — table → cards (≤920px) ============== */
@media (max-width: 920px) {
  .filter-strip, .list-wrap { padding-left: 20px; padding-right: 20px; }
  /* Mobile: lay the filter bar out as a 2-col grid so each form-group spans
     a full row and the Réinitialiser / Filtrer buttons share the last row
     (equal width, perfectly aligned). */
  .filter-bar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    padding: 14px;
  }
  .filter-bar .form-group {
    grid-column: 1 / -1;
    flex-wrap: wrap;
    margin: 0;
    /* Center the floating .control-label above its full-width .form-select,
       to match the centered headers/rows of the .mob-sort card below. */
    justify-content: center;
  }
  .filter-bar .form-group .form-select { width: 100%; }
  /* Half-width in the 2-col actions row : keep the icon + label on ONE line
     ("↺ Réinitialiser" would otherwise break at its space) and trim the side
     padding so the label fits without widening the column past the card. */
  .filter-bar .btn {
    width: 100%; justify-content: center;
    white-space: nowrap; padding-left: 12px; padding-right: 12px;
  }
  /* The grouped actions span the full row and lay their buttons in equal
     columns — two side by side (movie list) or one full-width (homework) via
     grid-auto-flow, so the same rule fits every .filter-bar. */
  .filter-bar .filter-actions {
    grid-column: 1 / -1;
    margin-left: 0;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 10px;
  }
  /* Homework filter opens with a "Filtrer" caption — center it on its own row. */
  .filter-bar-homework .lbl {
    grid-column: 1 / -1;
    text-align: center;
  }

  /* Mobile sort bar — same card aesthetic as .filter-bar above:
     paper-card surface, 1px rule border, 16px radius, 14px inner padding,
     20px horizontal gutters and a 14px gap to the list below. */
  .mob-sort {
    display: block;
    margin: 0 20px 14px;
    padding: 14px;
    background: var(--paper-card);
    border: 1px solid var(--rule);
    border-radius: 16px;
  }
  .mob-sort-label {
    font-family: var(--font-ui); font-weight: 900;
    font-size: 11px; letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-dim);
    margin-bottom: 8px;
    display: flex; align-items: center; gap: 10px;
    justify-content: center;
  }
  /* Symmetric hairlines flanking the centered "Trier par" label. */
  .mob-sort-label::before,
  .mob-sort-label::after {
    content: ""; flex: 1; height: 1px; background: var(--rule);
  }
  /* Each sort group on its own row: general, then one row per user */
  .mob-sort-row {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 8px;
    justify-content: center;
  }
  .mob-sort-row:last-child { margin-bottom: 0; }
  .mob-sort-row-general { padding-bottom: 6px; border-bottom: 1px dashed var(--rule); margin-bottom: 12px; }
  /* The user label takes its own full-width line so the FAV / NOTE / DATE trio
     below it stays on ONE line (at ~360px, label + 3 pills won't fit on a single
     row and DATE would otherwise wrap off on its own). */
  .mob-sort-rowlabel {
    display: inline-flex; align-items: center; gap: 8px;
    flex: 0 0 100%; justify-content: center;
    font-family: var(--font-ui); font-weight: 900;
    font-size: 11px; letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-dim);
  }
  .mob-sort-rowlabel .ava {
    position: relative;
    width: 22px; height: 22px; border-radius: 50%;
    display: grid; place-items: center;
    font-family: var(--font-display);
    font-style: italic; font-weight: 600; font-size: 11px;
    color: #fefdfa; overflow: hidden;
    background: var(--paper-soft);
  }
  .mob-sort-rowlabel .ava img {
    position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: cover;
  }
  .sort-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 14px; background: var(--paper-card);
    border: 1px solid var(--rule); border-radius: 999px;
    font-family: var(--font-ui); font-weight: 900;
    font-size: 11px; letter-spacing: .12em;
    text-transform: uppercase; color: var(--ink-mid);
    cursor: pointer; white-space: nowrap; flex-shrink: 0;
    transition: all .15s;
  }
  .sort-pill:hover { border-color: var(--violet); color: var(--violet); }
  /* Active pill: user pills carry --u (their theme colour); the general
     Titre / Moyenne pills have none and fall back to --ink. */
  .sort-pill.is-active {
    background: var(--u, var(--ink)); border-color: var(--u, var(--ink)); color: var(--paper-card);
  }
  .sort-pill .dir { font-size: 10px; opacity: .85; }
  /* Inactive pills show a faint ⇅ placeholder (injected by syncPillsToOrder)
     so the .dir slot keeps the exact same width whether sorted or not —
     clicking a sort option no longer shifts neighbouring pills. */
  .sort-pill:not(.is-active) .dir { opacity: .35; }

  /* Card mode for the table */
  .list { display: block; min-width: 0; }
  .list thead { display: none; }
  .list tbody { display: block; }

  .list tbody tr {
    display: grid;
    /* Fixed label + star, then note/date columns that hold their content
       (min 72px = the "Devoir" button, 44px = a year) without forcing the grid
       wider than the card — at ~360px the old 92/56 + 1fr 1fr overflowed, so the
       average score and the dates were clipped at the card's right edge. */
    grid-template-columns: 1fr 24px 66px 40px;
    grid-auto-flow: row dense;
    align-items: center;
    column-gap: 10px; row-gap: 10px;
    padding: 18px 18px 14px;
    /* !important to override DataTables' `table.dataTable > tbody > tr
       { background-color: transparent }` which has higher specificity. */
    background: var(--paper-card) !important;
    border: 1px solid var(--rule);
    border-radius: 14px;
    margin-bottom: 16px;
    position: relative;
    overflow: hidden;
  }
  .list tbody tr:hover { background: var(--paper-card); }

  .list tbody td {
    display: block; padding: 0; border-bottom: 0;
    text-align: left; box-shadow: none !important;
    border-left: 0 !important;
  }
  /* Place each cell type into its column — grid-auto-flow stacks user rows. */
  .list tbody td.cell-fav  { grid-column: 1 / 3; }
  .list tbody td.cell-mark { grid-column: 3 / 4; text-align: center;
                              display: flex; align-items: center; justify-content: center;
                              min-height: 28px; }
  .list tbody td.cell-when { grid-column: 4 / 5; text-align: center;
                              display: flex; align-items: center; justify-content: center;
                              min-height: 28px; }

  .list tbody td.t-title {
    grid-column: 1 / -1;
    padding: 0 0 14px;
    border-bottom: 1px solid var(--rule-soft);
    margin-bottom: 4px;
  }
  /* Empty state — DataTables emits a single <td colspan> for "no data". Inside
     the card grid it would otherwise sit in the first 78px track and read as
     left-aligned; span the whole row so the message stays centred. */
  .list tbody td.dt-empty {
    grid-column: 1 / -1;
  }
  .cell-title .poster { width: 46px; height: 68px; }
  .cell-title .tname { font-size: 17px; }

  /* Moyenne cell */
  .list tbody td.t-avg {
    grid-column: 1 / -1;
    display: flex; justify-content: space-between; align-items: baseline;
    padding: 0 0 12px;
    border-bottom: 1px solid var(--rule-soft);
    margin-bottom: 6px;
  }
  .list tbody td.t-avg::before {
    content: "Moyenne";
    font-family: var(--font-ui); font-weight: 900;
    font-size: 11px; letter-spacing: .08em;
    text-transform: uppercase; color: var(--ink-dim);
  }

  /* The base `tr:last-child td { border-bottom: 0 !important }` (which neutralises
     DataTables' last-row border) also strips the last card's title/moyenne
     dividers — re-add them so the last card matches every other card. */
  .list tbody tr:last-child td.t-title,
  .list tbody tr:last-child td.t-avg { border-bottom: 1px solid var(--rule-soft) !important; }

  /* User row layout (cell-fav becomes user-row anchor with label) */
  .list tbody td.cell-fav {
    display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 10px;
    position: relative;
  }
  .list tbody td.cell-fav::before {
    content: attr(data-user);
    font-family: var(--font-ui); font-weight: 900;
    font-size: 11px; letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-dim);
    min-width: 0; width: 100%;
    text-align: right;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .cell-mark-val { font-size: 18px; }
  .cell-mark-val .empty, .cell-when-val .empty { font-size: 13px; }

  /* Mobile: each film TR is already its own card, so drop the card styling
     on the DataTables wrapper to avoid a card-inside-a-card look. */
  .dt-layout-row.dt-layout-table {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    overflow: visible;
    margin: 0;
  }

  /* Mobile: replace the per-column tint with a rounded pill behind the
     current user's row. The pseudo is anchored to cell-fav and extended
     just enough to cover the label + the three data cells. */
  .list tbody td.my-col { background: transparent !important; }
  .list tbody td.cell-fav.my-col { z-index: 0; }
  .list tbody td.cell-fav.my-col::after {
    content: "";
    position: absolute;
    /* cell-fav now starts at the card's left content edge (col1), so the pill
       only needs a small negative inset to reach the card edge; width spans the
       whole row (past the date column) via the viewport, minus a small inset. */
    left: -9px;
    width: calc(100vw - 60px);
    top: -2px; bottom: -2px;
    border-radius: 10px;
    z-index: -1;
    /* Decorative tint pill — must not capture taps meant for the Noter
       button in the sibling .cell-mark (z-index: -1 alone isn't enough:
       hit testing still attributes the area to .cell-fav.my-col). */
    pointer-events: none;
  }
}


/* ==========================================================================
   [DISPLAY_SHOW] — single show detail (display_show.html)
   Movie / TV show / video game; the videogame variant adds the `.releases`
   block with per-region release dates.
   Note: per-user halo colors (.user-<id>) are generated by Jinja inline in
   the template — they live alongside the review markup.
   ========================================================================== */

/* ----- prev/next navigation ----- */
/* Three fixed zones — prev (left) / random (centre) / next (right) — via a
   1fr auto 1fr grid: the random button stays centred and the prev/next pills
   keep a fixed size and position even when one of them is absent (the empty
   column still holds its 1fr share). */
.show-nav {
  max-width: 1280px;
  margin: 0 auto;
  padding: 24px 56px 0;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  font-size: 11px;
}
.show-nav > span { display: flex; min-width: 0; }
.show-nav > span:first-child { justify-content: flex-start; }
.show-nav > span:last-child { justify-content: flex-end; }
.show-nav a {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 16px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--ink-mid);
  cursor: pointer;
  transition: color .15s, border-color .15s;
}
.show-nav a:hover { color: var(--violet); border-color: var(--violet); }
/* Fixed, symmetric prev/next pills (240px on desktop, capped to the column on
   mobile). The title ellipsises inside while the arrow always stays visible. */
.show-nav > span a { width: 240px; max-width: 100%; justify-content: center; }
/* flex:0 1 auto (not 1) so the arrow+title group is content-sized and stays
   centred by justify-content; it still shrinks + ellipsises for long titles. */
.show-nav .nav-title {
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.show-nav .nav-arrow { flex: 0 0 auto; }

/* ----- hero ----- */
.hero {
  display: grid;
  grid-template-columns: 360px 1fr;
  gap: 64px;
  padding: 56px 56px 24px;
  max-width: 1280px;
  margin: 0 auto;
  position: relative;
  isolation: isolate;
}
.hero::before {
  content: ""; position: absolute; left: 56px; right: 56px; bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--ink-dim) 30%, var(--ink-dim) 70%, transparent);
}
/* poster */
.poster-wrap { position: relative; }
.poster {
  aspect-ratio: 2/3;
  border-radius: 4px;
  overflow: hidden;
  position: relative;
  box-shadow:
    0 30px 60px -22px rgba(28, 24, 32, .4),
    0 18px 40px -18px color-mix(in srgb, var(--show-accent, rgb(var(--violet-rgb))) 45%, transparent),
    inset 0 0 0 1px rgba(0, 0, 0, .04);
  transform: rotate(-1.2deg);
  transition: transform .6s cubic-bezier(.2,.7,.2,1);
}
.poster:hover { transform: rotate(0); }
.poster-image {
  position: absolute; inset: 0;
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 1;
}
.poster::after {
  content: ""; position: absolute; inset: 0;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, .04) 0 1px, transparent 1px 80px);
  pointer-events: none; z-index: 2;
}

/* title row + favorite button */
/* Grid (not flex) so a long wrapping title keeps the favorite beside it on
   its row while the original title always sits on its own line — flex would
   let the favorite wrap under a long title. The original is a DOM child of the
   row (moved after the h1) so mobile can reorder it between title and favorite. */
.title-row {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: "title fav" "original original";
  align-items: center;
  column-gap: 28px; row-gap: 4px;
  margin-bottom: 4px;
}
.title-row .title { grid-area: title; margin-bottom: 0; min-width: 0; }
.title-row .fav-btn { grid-area: fav; }
.title-row .original { grid-area: original; }

.fav-btn {
  border-left: 1px solid var(--rule);
  padding: 0 0 0 26px;
  color: var(--violet);
  transition: border-color .25s;
  position: relative;
}
.fav-trigger {
  background: transparent; border: 0; padding: 0;
  display: inline-flex; flex-direction: column; align-items: center;
  gap: 10px;
  color: inherit;
  cursor: pointer;
  font-family: inherit;
}
.fav-btn .fav-icon {
  width: 72px; height: 72px;
  border-radius: 50%;
  border: 2px solid currentColor;
  background: var(--paper-card);
  display: grid; place-items: center;
  padding-bottom: 8px;
  font-size: 60px; line-height: 1;
  transition: background .25s, transform .25s, box-shadow .25s, color .25s;
  box-shadow:
    0 0 0 5px rgba(var(--violet-rgb),.07),
    0 16px 34px -14px rgba(var(--violet-rgb),.35);
}
.fav-btn.is-empty .fav-icon { color: var(--ink-dim); }
.fav-btn:hover { border-color: var(--violet); }
.fav-trigger:hover .fav-icon {
  background: var(--violet);
  color: var(--paper-card);
  transform: scale(1.05);
  box-shadow:
    0 0 0 8px rgba(var(--violet-rgb),.14),
    0 22px 42px -14px rgba(var(--violet-rgb),.55);
}
.fav-btn .fav-tag {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .18em;
  text-transform: uppercase;
  color: inherit; white-space: nowrap;
  display: inline-block; min-width: 140px;
  text-align: center;
}
.fav-btn.is-empty .fav-tag { color: var(--ink-dim); }

/* favorite menu (dropdown) */
.fav-menu {
  position: absolute;
  top: calc(100% + 12px); right: 0;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: 0 10px 28px rgba(28, 24, 32, .14), 0 2px 6px rgba(28, 24, 32, .08);
  padding: 12px;
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  width: max-content;
  opacity: 0; pointer-events: none;
  transform: translateY(-4px);
  transition: opacity .15s, transform .15s;
  z-index: 50;
}
.fav-btn.is-open .fav-menu {
  opacity: 1; pointer-events: auto;
  transform: translateY(0);
}
.fav-menu a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  padding: 14px 16px;
  border-radius: 10px;
  font-size: 10px;
  text-align: center;
  cursor: pointer;
  min-width: 120px;
  transition: background .15s, color .15s, transform .15s;
}
.fav-menu a:hover { background: var(--paper-soft); color: var(--ink); transform: translateY(-2px); }
.fav-menu a .star {
  display: grid; place-items: center;
  width: 60px; height: 60px;
  font-size: 60px; line-height: 1;
  transition: transform .15s;
}
.fav-menu a:hover .star { transform: scale(1.08); }
.fav-menu a.empty .star { color: var(--ink-dim); }
.fav-menu a[data-star="favorite_star"] .star { color: var(--warn); }
.fav-menu a[data-star="homework_star"] .star { color: var(--accent-rose); }
.fav-menu a[data-star="mustsee_star"] .star { color: var(--violet); }
.fav-menu a[data-star="random_homework_star"] .star { color: var(--accent-teal); }

/* eyebrow / title / original / byline */
.show-detail .eyebrow {
  font-size: 13px;
  margin-bottom: 18px;
}
.show-detail .eyebrow .accent { color: var(--show-accent, var(--violet)); font-weight: 900; }
.show-detail .title {
  font-family: var(--font-display);
  font-weight: 400; font-size: clamp(48px, 6vw, 92px);
  line-height: .98; letter-spacing: -.04em;
  margin-bottom: 4px; color: var(--ink);
}
.show-detail .title em { font-style: italic; font-weight: 500; }
.show-detail .original {
  font-family: var(--font-display);
  font-style: italic; font-weight: 400; font-size: 19px;
  color: var(--ink-mid); margin-bottom: 38px;
  letter-spacing: -.015em;
}
.show-detail .original::before { content: "« "; opacity: .5; }
.show-detail .original::after { content: " »"; opacity: .5; }

.byline {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 22px 28px;
  font-family: var(--font-ui);
  font-size: 12px; letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-mid);
  padding: 22px 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin-bottom: 36px;
  font-weight: 900;
}
.byline > div { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.byline label { color: var(--ink-dim); font-weight: 900; font-size: 11px; display: inline-flex; align-items: center; gap: 7px; }
.byline label i { font-size: 11px; opacity: .8; }
.byline value {
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 500; font-size: 16px;
  letter-spacing: -.005em;
  text-transform: none;
}
.byline value a { color: var(--violet); }
.byline value a:hover { text-decoration: underline; }
.byline value .sep { color: var(--ink-dim); margin: 0 .35em; font-weight: 400; }
.byline .pill-status {
  display: inline-block; padding: 3px 12px; text-align: center;
  border-radius: 999px; font-size: 12px;
  font-family: var(--font-ui); font-weight: 900;
  letter-spacing: .06em; text-transform: uppercase;
  background: var(--paper-soft); color: var(--ink-mid);
  border: 1px solid var(--rule);
}
.byline .pill-status.ongoing { color: var(--ok); border-color: var(--ok); }
.byline .pill-status.ended { color: var(--ink-mid); }
.byline .pill-status.canceled { color: var(--error); border-color: var(--error); }

.synopsis {
  font-family: var(--font-body);
  font-weight: 420; font-size: 20px;
  line-height: 1.55; letter-spacing: -.005em;
  color: var(--ink);
  margin-bottom: 40px;
  max-width: 64ch;
  position: relative; padding-left: 26px;
}
.synopsis::before {
  content: "“"; position: absolute; left: -8px; top: -22px;
  font-family: var(--font-display);
  font-size: 96px; line-height: 1; font-style: italic; font-weight: 500;
  color: var(--violet); opacity: .55;
}
/* DeepL translation credit — sits just under the synopsis, right-aligned and
   italic, as a discreet source note (same text box as .synopsis so its right
   edge lines up). The :has rule pulls the synopsis's bottom margin in when the
   credit follows, keeping the gap to the action buttons unchanged for movies
   (no credit). */
.synopsis:has(+ .synopsis-translated) { margin-bottom: 8px; }
.synopsis-translated {
  /* Match the synopsis box width so the right-aligned credit lines up with the
     synopsis's right edge. `ch` scales with font-size, so 64ch at this 13px
     credit would be narrower than 64ch at the 20px synopsis — scale it back up
     by the font-size ratio (20/13) to get the same rendered width. */
  max-width: calc(64ch * 20 / 13);
  padding-left: 26px;
  margin-bottom: 40px;
  font-family: var(--font-body);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-dim);
  text-align: right;
}

/* small action buttons row (Update show / Mark / Slack) */
.show-actions {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  margin-top: 18px;
}
/* ----- videogame: per-region release dates ----- */
.releases {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 26px 30px;
  margin-bottom: 36px;
  position: relative;
  box-shadow: 0 1px 0 var(--rule-soft);
}
.releases-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 18px; padding-bottom: 14px;
  border-bottom: 1px dashed var(--rule);
}
.releases-head h3 {
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 22px;
  letter-spacing: -.025em; color: var(--ink);
}
.releases-meta {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-dim);
}
/* world-premiere highlight — punchy violet bar, the earliest release of all */
.releases-premiere {
  margin-bottom: 22px;
  padding: 13px 22px;
  border-radius: 12px;
  background: linear-gradient(118deg, var(--violet), #4a2770);
  color: #f7f4f1;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
}
.premiere-eyebrow {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--violet-soft);
}
.premiere-date {
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 23px;
  letter-spacing: -.02em; color: #fefdfa;
}
/* releases table — Date / Région / Plateformes, one row per release */
.releases-table {
  width: 100%;
  border-collapse: collapse;
}
.releases-table th {
  text-align: left;
  padding: 0 18px 10px 0;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-dim);
  border-bottom: 1px solid var(--rule);
}
.releases-table td {
  padding: 12px 18px 12px 0;
  border-bottom: 1px solid var(--rule-soft);
  vertical-align: baseline;
}
.releases-table tr:last-child td { border-bottom: 0; padding-bottom: 2px; }
.rt-date {
  font-family: var(--font-ui); font-weight: 600;
  font-size: 14px; color: var(--ink);
  white-space: nowrap;
  font-feature-settings: "tnum" 1;
}
.rt-region {
  white-space: nowrap;
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 16px;
  color: var(--ink);
}
.rt-region .fi {
  display: inline-block;
  width: 24px; height: 17px;
  background-size: cover; background-position: center;
  border: 1px solid var(--rule);
  border-radius: 2px;
  margin-right: 9px; vertical-align: -3px;
}
.rt-platforms {
  font-family: var(--font-body);
  font-size: 14.5px; color: var(--ink); line-height: 1.5;
}

/* ----- reviews ----- */
.reviews {
  max-width: 1280px;
  margin: 0 auto;
  padding: 12px 56px 80px;
}
.reviews-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 32px;
}
.reviews-title {
  font-family: var(--font-display);
  font-weight: 500; font-style: italic; font-size: 32px;
  letter-spacing: -.025em; color: var(--ink);
}
.reviews-count {
  font-size: 12px;
}
.reviews-toggle {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase;
  padding: 8px 14px; border: 1px solid var(--rule);
  background: var(--paper-card); border-radius: 999px;
  color: var(--ink-mid); cursor: pointer;
  transition: all .15s;
}
.reviews-toggle:hover { border-color: var(--violet); color: var(--violet); }

.review {
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 8px;
  margin-bottom: 16px;
  position: relative;
  overflow: hidden;
  transition: border-color .3s, box-shadow .3s;
  box-shadow: 0 1px 0 var(--rule-soft);
}
.review:hover {
  border-color: var(--violet);
  box-shadow: 0 18px 30px -18px rgba(var(--violet-rgb),.25);
}
.review-header {
  display: grid;
  grid-template-columns: 88px 1fr 172px;
  grid-template-areas:
    "avatar meta    score"
    "avatar comment score";
  column-gap: 32px; row-gap: 10px;
  align-items: start;
  padding: 28px 28px;
  position: relative;
}
.review-header > .avatar-large { grid-area: avatar; }
.review-header > .review-meta { grid-area: meta; min-width: 0; }
.review-header > .review-comment { grid-area: comment; min-width: 0; }
.review-header > .score-block { grid-area: score; }
.review-header::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(60% 100% at 0 50%, var(--halo, transparent), transparent 70%);
  pointer-events: none;
}
.review-header > * { position: relative; z-index: 1; }

.avatar-large {
  width: 72px; height: 72px; font-size: 32px;
  letter-spacing: -.02em;
  background: var(--paper-soft);
  border: 1px solid rgba(0, 0, 0, .04);
}

/* One single header line carrying everything: name + (optional) devoir
   badge + giver + date + where + favstar + comment-count. Wraps only
   if the viewport truly can't fit it. comment-count is pinned to the
   right via margin-left:auto (see rule below). */
.review-meta {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px 12px;
  font-family: var(--font-ui);
  font-size: 12px; letter-spacing: .14em;
  text-transform: uppercase;
  font-weight: 900;
}
.review-name {
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 22px;
  letter-spacing: -.025em;
  color: var(--ink); text-transform: none;
  margin-right: 4px;
}
.review-when { color: var(--ink-dim); font-weight: 900; }
.review-where {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--ink-mid);
  padding: 3px 9px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper);
  font-weight: 900;
}
.review-favstar {
  display: inline-flex; align-items: center;
  color: var(--ink-dim); font-size: 30px; line-height: 1;
  margin-left: 2px;
}
.review-comment {
  font-family: var(--font-body);
  font-weight: 400; font-size: 16px; line-height: 1.5;
  color: var(--ink); /* slightly darker than mockup for HTML-rendered comments */
  overflow-wrap: break-word; /* break a long unbroken run (URL, "SECOUUU…URS") instead of overflowing on mobile */
  -webkit-hyphens: auto; hyphens: auto; /* hyphenate real words at valid FR points (lang="fr") */
}
.review-comment.empty {
  color: var(--ink-dim); font-size: 14px; font-style: italic;
  display: flex; align-items: center; gap: 8px;
}
.review-comment.empty i { font-size: 14px; opacity: .6; }
.review-comment :is(p, ul, ol, blockquote) { margin: 0 0 10px; }
.review-comment :is(p, ul, ol, blockquote):last-child { margin-bottom: 0; }
/* Compact homework attribution chip : the giver's circular avatar with
   a small 📝 sticker in the lower-right corner. Sits inline in the
   review meta row so it costs almost no horizontal space. Hover/long-
   press surfaces "Devoir par <nickname>" via the title attribute. */
.review-devoir-ava {
  position: relative;
  width: 22px; height: 22px;
  flex: 0 0 22px;
  border-radius: 50%;
  display: inline-grid; place-items: center;
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 11px;
  letter-spacing: -.02em;
  color: #fefdfa;
  background: var(--paper-soft);
  overflow: visible;
  cursor: help;
}
.review-devoir-ava > img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  border-radius: 50%;
}
.review-devoir-ava .badge {
  position: absolute;
  right: -4px; bottom: -4px;
  width: 14px; height: 14px;
  background: rgba(255, 200, 0, .95);
  border: 1px solid #d49a00;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 8px; line-height: 1;
  color: #8a6500;
  font-style: normal;
}

.score-block {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  border-left: 1px solid var(--rule);
  padding-left: 28px;
  align-self: stretch; justify-content: center;
}
.score-block .btn-show { width: 100%; justify-content: center; }
.score-block .score-empty { max-width: 100%; text-align: center; }
.score {
  display: inline-flex; align-items: baseline; gap: 5px;
  font-family: var(--font-ui);
  font-weight: 600; font-size: 56px; line-height: 1;
  color: var(--violet); letter-spacing: -.015em;
  font-feature-settings: "tnum" 1;
}
.score-empty {
  font-size: 11px;
  padding: 10px 16px;
  border: 1px dashed var(--rule);
  border-radius: 999px;
  background: var(--paper);
}

/* ----- Comment-count + Add-comment pills (review header, right side) ----- */
.comment-count {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--paper-card); color: var(--ink-mid);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.comment-count:hover { border-color: var(--violet); color: var(--violet); }
.comment-count .num {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 13px; letter-spacing: 0;
}
.comment-count .arrow {
  display: inline-block; transition: transform .2s;
  transform: rotate(180deg);
}
.review.is-collapsed .comment-count .arrow { transform: rotate(0); }

/* ----- Thread (collapsable, sibling of review-header inside .review) ----- */
.thread {
  border-top: 1px dashed var(--rule);
  padding: 0 28px;
  display: flex; flex-direction: column;
  max-height: 1500px;
  transition: max-height .35s ease, padding .25s ease;
  overflow: hidden;
}
.review.is-collapsed .thread {
  max-height: 0;
  padding-top: 0; padding-bottom: 0;
  border-top-color: transparent;
}

.thread-list {
  padding: 14px 0 18px;
  display: flex; flex-direction: column; gap: 14px;
}
.thread-empty {
  padding: 22px 0 8px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-display);
  font-style: italic; font-weight: 400; font-size: 15.5px;
  color: var(--ink-dim);
  text-align: center;
}
.thread-empty .thread-empty-illu {
  width: 96px; height: 68px;
  color: var(--ink-dim); opacity: .55;
}
.thread-empty .quote {
  font-family: var(--font-display);
  font-weight: 400; font-size: 28px; line-height: .8;
  color: var(--violet); opacity: .55;
}
.thread-empty .msg { line-height: 1.3; }

/* ----- Subcomment (one comment in the thread) ----- */
.subcomment {
  display: grid; grid-template-columns: 40px 1fr;
  gap: 12px; padding: 0;
  position: relative;
}
.subcomment .ava {
  width: 40px; height: 40px; font-size: 16px;
  background: var(--paper-soft);
}
.subcomment .body {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  border-radius: 14px;
  border-top-left-radius: 4px;
  padding: 10px 14px;
  font-family: var(--font-body); font-size: 14.5px;
  color: var(--ink); line-height: 1.5;
  overflow-wrap: break-word; /* same as .review-comment: a long unbroken word must wrap, not overflow */
  -webkit-hyphens: auto; hyphens: auto; /* hyphenate real words at valid FR points (lang="fr") */
}
.subcomment.is-mine .body {
  background: var(--paper-card);
  border-color: var(--rule);
}
/* The colored border-left is set per user via dynamic Jinja-generated CSS in
   the page template (.subcomment.user-<id> .body { border-left: 3px solid <color>; }) */

.subcomment .meta {
  display: flex; align-items: baseline; gap: 10px; margin-bottom: 3px;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase;
}
.subcomment .meta .who { color: var(--ink); }
.subcomment .meta .when { color: var(--ink-dim); font-weight: 500; }
.subcomment .text { font-size: 14px; }

/* Inline edit form: input + save + cancel */
.subcomment .comment_edit_form {
  display: flex; align-items: center; gap: 8px;
  margin-top: 4px;
}
.subcomment .text.editing {
  flex: 1;
  background: rgba(var(--violet-rgb),.05);
  border: 1px solid var(--violet);
  border-radius: 8px;
  padding: 6px 10px;
  outline: none;
  font: inherit;
}
.subcomment .edit-save,
.subcomment .edit-cancel {
  display: inline-grid; place-items: center;
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 12px; color: var(--ink-mid);
  background: var(--paper-card);
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.subcomment .edit-save:hover { border-color: var(--violet); color: var(--paper-card); background: var(--violet); }
.subcomment .edit-cancel:hover { border-color: var(--accent-rose); color: var(--accent-rose); }

/* Edit / Delete buttons — top-right of body, oval pill, visible on subcomment hover */
.subcomment .tools {
  position: absolute; top: 6px; right: 8px;
  display: none; gap: 4px;
}
.subcomment:hover .tools { display: flex; }
.subcomment .tools a {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase;
  padding: 4px 11px; border-radius: 999px;
  cursor: pointer; text-decoration: none;
  border: 1px solid transparent;
  transition: background .15s, color .15s, border-color .15s;
}
.subcomment .tools a i { font-size: 10px; }
.subcomment .tools .edit_comment {
  background: var(--violet); color: var(--paper-card);
  border-color: var(--violet);
}
.subcomment .tools .edit_comment:hover {
  background: #4a2870; border-color: #4a2870;
}
.subcomment .tools .delete_comment {
  background: var(--accent-rose); color: var(--paper-card);
  border-color: var(--accent-rose);
}
.subcomment .tools .delete_comment:hover {
  background: #8c2f47; border-color: #8c2f47;
}

/* ----- Composer (always visible inside the open thread) ----- */
.composer {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: 12px; align-items: center;
  padding: 14px 28px 18px;
  margin: 6px -28px 0;
  border-top: 1px dashed var(--rule);
}
.composer .ava {
  width: 40px; height: 40px; font-size: 16px;
  background: linear-gradient(135deg, var(--violet-soft), var(--violet));
}
.composer-input {
  display: flex; align-items: center; gap: 8px;
  min-width: 0; /* shrink below content in the 1fr grid column instead of pushing
                   the Send button off-screen on narrow viewports */
  padding: 6px 6px 6px 14px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 14px;
  transition: border-color .15s;
}
.composer-input:focus-within { border-color: var(--violet); }
.composer-input input {
  flex: 1; min-width: 0; border: 0; background: transparent; outline: none;
  font: inherit; font-size: 14.5px; color: var(--ink);
  font-family: var(--font-body);
  padding: 4px 0;
}
.composer-input input::placeholder { color: var(--ink-dim); }
.composer .send {
  flex-shrink: 0;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 16px;
  background: var(--ink); color: var(--paper);
  border: 1px solid var(--ink); border-radius: 999px;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer;
  transition: all .2s;
}
.composer .send:hover { background: var(--violet); border-color: var(--violet); color: var(--paper); }
.composer .send:disabled {
  opacity: .4; cursor: not-allowed; background: var(--ink-dim);
}

/* edit form variant — inline replacement of the comment text */
.subcomment .composer-input { background: var(--paper-card); border-color: var(--violet); }

/* ============== MARK STUDIO ==============
   Compact rating card : score feature (left column) + critique (right),
   with a footer row for the date / venue meta and the submit actions. */
.mark-studio {
  max-width: 1280px; margin: 0 auto;
  padding: 12px 56px 80px;
}
.mark-studio-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 22px;
}

.mark-studio .mark-form {
  overflow: hidden;
}
.mark-studio .form-group { margin: 0; }
.mark-studio .control-label,
.mark-studio label.control-label {
  display: block;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 9px;
}
.mark-studio input[type="text"],
.mark-studio input[type="number"],
.mark-studio input[type="date"],
.mark-studio select,
.mark-studio textarea {
  width: 100%;
  padding: 10px 13px;
  border: 1px solid var(--rule);
  border-radius: 9px;
  background: var(--paper-card);
  font-family: var(--font-body); font-size: 15px;
  color: var(--ink); outline: none;
  transition: border-color .2s, box-shadow .2s;
}
.mark-studio input:focus,
.mark-studio select:focus,
.mark-studio textarea:focus {
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(var(--violet-rgb),.12);
}
.mark-studio .help-block {
  font-family: var(--font-ui); font-weight: 600;
  font-size: 11px; letter-spacing: .04em;
  color: var(--error); margin-top: 6px;
}

/* ---- two columns : score feature | critique ---- */
.mark-studio .mark-grid {
  display: grid;
  grid-template-columns: 252px 1fr;
}
.mark-studio .mark-score {
  border-right: 1px solid var(--rule);
  background: var(--paper-soft);
  padding: 30px 26px 26px;
  display: flex; flex-direction: column; align-items: center;
}
.mark-studio .mark-score-eyebrow {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-dim);
}
.mark-studio .mark-score-num {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500;
  font-size: 78px; line-height: 1;
  letter-spacing: -.045em;
  color: var(--violet);
  margin-top: 4px;
  font-feature-settings: "tnum" 1;
}
.mark-studio .mark-score-max {
  font-size: 11px;
  margin-bottom: 24px;
}
.mark-studio .mark-score.has-error .mark-score-num { color: var(--error); }
.mark-studio .mark-score .help-block { text-align: center; }

.mark-studio .mark-slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 12px;
  background:
    linear-gradient(90deg,
      transparent calc(25% - 1px), rgba(254, 253, 250, .14) calc(25% - 1px),
      rgba(254, 253, 250, .14) calc(25% + 1px), transparent calc(25% + 1px),
      transparent calc(50% - 1px), rgba(254, 253, 250, .14) calc(50% - 1px),
      rgba(254, 253, 250, .14) calc(50% + 1px), transparent calc(50% + 1px),
      transparent calc(75% - 1px), rgba(254, 253, 250, .14) calc(75% - 1px),
      rgba(254, 253, 250, .14) calc(75% + 1px), transparent calc(75% + 1px)),
    linear-gradient(90deg,
      var(--violet) 0%, #8a4bbf var(--val, 0%),
      var(--ink) var(--val, 0%), var(--ink) 100%);
  border-radius: 999px; outline: none; cursor: pointer;
  margin: 0; padding: 0; border: 0;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25);
}
.mark-studio .mark-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 30px; height: 30px; border-radius: 50%;
  background: radial-gradient(circle at 30% 25%, #ffffff 0%, var(--paper-card) 60%);
  border: 4px solid var(--violet);
  box-shadow: 0 0 0 5px rgba(var(--violet-rgb),.15), 0 6px 16px rgba(var(--violet-rgb),.5);
  cursor: grab; transition: transform .15s ease, box-shadow .2s ease;
}
.mark-studio .mark-slider:focus::-webkit-slider-thumb,
.mark-studio .mark-slider::-webkit-slider-thumb:hover {
  transform: scale(1.12);
  box-shadow: 0 0 0 8px rgba(var(--violet-rgb),.2), 0 10px 24px rgba(var(--violet-rgb),.6);
}
.mark-studio .mark-slider::-webkit-slider-thumb:active { cursor: grabbing; }
.mark-studio .mark-slider::-moz-range-thumb {
  width: 30px; height: 30px; border-radius: 50%;
  background: radial-gradient(circle at 30% 25%, #ffffff 0%, var(--paper-card) 60%);
  border: 4px solid var(--violet);
  box-shadow: 0 0 0 5px rgba(var(--violet-rgb),.15), 0 6px 16px rgba(var(--violet-rgb),.5);
  cursor: grab;
}
.mark-studio .mark-slider-ticks {
  display: flex; justify-content: space-between;
  width: 100%;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10px; letter-spacing: .12em;
  color: var(--ink-dim);
  padding: 0 2px; margin-top: 10px;
}

/* ---- right : the critique ---- */
.mark-studio .mark-critique { padding: 26px 28px; }
.mark-studio .mark-critique textarea {
  min-height: 200px;
  background-color: var(--paper);
  background-image: repeating-linear-gradient(transparent 0 31px, rgba(var(--violet-rgb),.06) 31px 32px);
  border-style: dashed; border-radius: 6px;
  font-family: var(--font-body); font-size: 15px; line-height: 32px;
  padding: 4px 14px; resize: vertical;
}
.mark-studio .mark-critique .ck-editor__main > .ck-editor__editable,
.mark-studio .mark-critique .ck.ck-content {
  min-height: 190px;
  font-family: var(--font-body); font-size: 15px; line-height: 1.7;
  color: var(--ink);
}
/* Let the editor toolbar wrap to a second row on narrow screens instead of
   clipping its rightmost buttons (lists) off-screen (mobile < ~410px). */
.mark-studio .mark-critique .ck.ck-toolbar,
.mark-studio .mark-critique .ck.ck-toolbar > .ck-toolbar__items {
  flex-wrap: wrap;
}

/* ---- footer : meta + actions ---- */
.mark-studio .mark-foot {
  border-top: 1px solid var(--rule);
  padding: 18px 28px;
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 26px; flex-wrap: wrap;
}
.mark-studio .mark-foot-meta {
  display: flex; gap: 26px; align-items: flex-end; flex-wrap: wrap;
}
/* Date field and seen-where pills share one explicit height so their labels and
   controls line up across browsers — native date inputs have a browser-dependent
   intrinsic height, so bottom-aligned unequal controls push a label out of line. */
.mark-studio .mark-foot-meta input[type="date"],
.mark-studio .mark-foot-meta .radio-pill { height: 43px; }
.mark-studio .mark-foot-meta .mark-field input[type="date"] { width: 170px; }
.mark-studio .mark-where-radios { display: flex; gap: 8px; flex-wrap: wrap; }
.mark-studio .radio-pill {
  position: relative;
  display: inline-flex; align-items: center;
  padding: 0 16px;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--paper-card);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-mid); cursor: pointer;
  transition: all .2s ease;
}
.mark-studio .radio-pill:hover { border-color: var(--violet); color: var(--violet); }
.mark-studio .radio-pill input[type="radio"] {
  position: absolute; opacity: 0; inset: 0; cursor: pointer;
  width: 100%; height: 100%; margin: 0;
}
.mark-studio .radio-pill:has(input[type="radio"]:checked) {
  background: var(--ink); border-color: var(--ink); color: var(--paper);
}
.mark-studio .mark-foot-actions { display: flex; gap: 12px; flex-wrap: wrap; }
.mark-studio .mark-foot-actions .btn {
  box-shadow: 0 8px 18px -10px rgba(28, 24, 32, .25);
  transition: transform .2s ease, box-shadow .2s ease, background .2s, border-color .2s, color .2s;
}
.mark-studio .mark-foot-actions .btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 26px -12px rgba(var(--violet-rgb),.4);
}

@media (max-width: 760px) {
  .mark-studio { padding: 0 16px 40px; }
  .mark-studio .mark-grid { grid-template-columns: 1fr; }
  .mark-studio .mark-score {
    border-right: 0; border-bottom: 1px solid var(--rule);
    padding: 22px;
  }
  .mark-studio .mark-score-num { font-size: 64px; }
  .mark-studio .mark-critique { padding: 22px; }
  .mark-studio .mark-foot { flex-direction: column; align-items: stretch; padding: 18px 22px; }
  /* Centre each meta field: the label and the control beneath it are both
     centred (date input under "Vu le", the where pills under "Où j'ai vu"). */
  .mark-studio .mark-foot-meta { flex-direction: column; align-items: stretch; gap: 18px; }
  .mark-studio .mark-foot-meta .mark-field { text-align: center; }
  .mark-studio .mark-foot-meta .mark-field input[type="date"] { display: inline-block; width: 170px; max-width: 100%; }
  .mark-studio .mark-foot-meta .mark-where-radios { justify-content: center; }
  .mark-studio .mark-foot-actions .btn { width: 100%; justify-content: center; }
}

/* ============== RESPONSIVE — display_show ============== */
@media (max-width: 920px) {
  .show-nav {
    padding: 16px 16px 0;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    gap: 8px;
    align-items: center;
  }
  .show-nav > span { display: flex; min-width: 0; }
  .show-nav > span:first-child { justify-content: flex-start; }
  .show-nav > span:last-child { justify-content: flex-end; }
  .show-nav a {
    max-width: 100%;
    padding: 7px 12px;
    font-size: 10px;
    letter-spacing: .1em;
  }
  .show-nav > a.random {
    width: 36px; height: 36px;
    padding: 0;
    font-size: 0;
    justify-content: center;
    flex-shrink: 0;
  }
  .show-nav > a.random::before {
    content: "↻";
    font-size: 16px; line-height: 1;
    color: inherit;
  }
  /* minmax(0,1fr) lets the column shrink below its content's min-content:
     without it, a very long title's widest word (plus the favorite button on
     the .title-row) forces the whole hero — poster included — wider than the
     viewport and it spills off the right. */
  .hero { grid-template-columns: minmax(0, 1fr); gap: 36px; padding: 32px 20px 48px; max-width: 100%; }
  .hero::before { left: 20px; right: 20px; }
  /* Technical info as a regular grid: 4 columns on desktop (base rule), 2 here. */
  .byline { grid-template-columns: repeat(2, 1fr); }
  /* On mobile the favorite button ALWAYS sits on its own line under the title
     (flex-basis:100% forces the wrap), for a consistent layout whatever the
     title length. Drop the vertical hairline separator (border-left) — it only
     made sense side-by-side; stacked it reads as a stray vertical stroke. The
     row gap provides the separation. */
  /* Single column, centered, in reading order: title → original → favorite.
     The original's desktop margin-bottom (meant to precede the byline) is
     dropped here since it's no longer the last element; row-gap spaces the three. */
  .title-row {
    grid-template-columns: 1fr;
    grid-template-areas: "title" "original" "fav";
    justify-items: center;
    row-gap: 22px; margin-bottom: 24px;
  }
  .title-row .title { text-align: center; }
  .title-row .original { margin-bottom: 0; text-align: center; }
  .title-row .fav-btn { border-left: 0; padding-left: 0; text-align: center; }
  /* Favorite dropdown: the desktop 3-column max-content menu (~547px) is wider
     than the phone and, anchored right:0 to a now-centred button, spilled off
     the left edge with its options unreachable. Recentre it under the button
     and constrain it to the viewport: 2 columns, "Aucun" spanning the top. */
  .fav-menu {
    grid-template-columns: repeat(2, 1fr);
    width: min(calc(100vw - 32px), 340px);
    left: 50%; right: auto;
    transform: translateX(-50%) translateY(-4px);
  }
  .fav-btn.is-open .fav-menu { transform: translateX(-50%) translateY(0); }
  .fav-menu a { min-width: 0; }
  .fav-menu a.empty { grid-column: 1 / -1; }
  .reviews { padding: 20px 20px 60px; max-width: 100%; }
  /* Stack the head so neither the title nor the toggle wraps mid-word: title
     (slightly smaller, keeping "Toutes les notes · N sur M" on one line) then
     the toggle button right-aligned on its own line below. */
  .reviews-head { flex-direction: column; align-items: flex-start; gap: 14px; }
  .reviews-title { font-size: 26px; }
  .reviews-toggle { white-space: nowrap; align-self: flex-end; }

  /* Stack the show action buttons (Mettre à jour / Noter / Publier) as
     full-width, centered pills — same rhythm as the Filtrer submit on
     the list pages. The first action sits inside an inline <form>, so
     stretch that too. */
  .show-actions { flex-direction: column; align-items: stretch; gap: 10px; }
  .show-actions > form { display: block !important; width: 100%; }
  .show-actions .btn { width: 100%; justify-content: center; }

  /* review-header — 3 cols : avatar 48px / body / score (fixed width on
     the right). The score column has a fixed width so the left separator
     always lands at the same X regardless of what the slot holds
     ("15.5" / "Pas notée" / "📝 Devoir"). Width 110px so "📝 Devoir"
     stays on a single line at the .btn font size. */
  /* Stack the review in three full-width rows so nothing is cramped in a narrow
     middle column: [avatar | meta] · comment · score. The meta then spans the
     full width (its chips — date, MAISON, ★, 💬 — align on one line) and the
     comment no longer stretches tall. The score moves to a centered footer. */
  .review-header {
    grid-template-columns: 48px 1fr;
    grid-template-areas:
      "avatar  meta"
      "comment comment"
      "score   score";
    column-gap: 12px; row-gap: 14px;
    padding: 18px;
  }
  .avatar-large { width: 48px; height: 48px; font-size: 20px; }
  .review-meta { gap: 6px 7px; font-size: 10px; letter-spacing: .12em; }
  .review-favstar { font-size: 16px; }
  /* Name on its own line so the chips below (date · where · ★ · 💬) share one
     row; comment-count drops margin-left:auto here so it flows next to the star
     instead of being pushed onto a line of its own. */
  .review-name { font-size: 18px; flex-basis: 100%; margin-right: 0; }
  .review-where { padding: 2px 7px; font-size: 10px; }
  .comment-count {
    margin-left: 0;
    padding: 3px 8px; min-height: 22px; min-width: 0;
    font-size: 10px;
  }
  .comment-count .num { font-size: 12px; }

  /* Full-width footer under the comment, separated by a top hairline. */
  .score-block {
    flex-direction: row; align-items: center; justify-content: center;
    gap: 12px; padding: 14px 0 0;
    border-left: 0;
    border-top: 1px solid var(--rule);
  }
  .score-block .btn { padding: 7px 14px; white-space: nowrap; }
  .score { font-size: 32px; }
  .score-empty { padding: 6px 10px; font-size: 10px; }

  /* Thread tighter on mobile */
  .thread { padding: 0 18px; }
  .thread-list { padding: 12px 0 14px; gap: 12px; }
  .subcomment { grid-template-columns: 32px 1fr; gap: 10px; }
  .subcomment .ava { width: 32px; height: 32px; font-size: 13px; }
  .subcomment .body { padding: 8px 12px; font-size: 14px; }
  .subcomment .text { font-size: 13.5px; }
  .subcomment .meta { font-size: 10px; gap: 8px; }
  /* show tools always on touch (no hover) */
  .subcomment .tools {
    position: static;
    display: flex; gap: 6px;
    margin-top: 6px;
  }
  .subcomment .tools button,
  .subcomment .tools a { font-size: 9px; padding: 3px 7px; }

  /* Composer — keep avatar + input row, send pill stays inline at end of input */
  .composer {
    grid-template-columns: 32px 1fr;
    row-gap: 8px;
    padding: 12px 18px 16px;
    margin: 6px -18px 0;
  }
  .composer .ava { width: 32px; height: 32px; font-size: 13px; }
  .composer-input { padding: 5px 5px 5px 12px; }
  .composer-input input { font-size: 14px; }
  .composer .send { padding: 7px 12px; font-size: 10px; gap: 4px; }

  /* ---- Videogame release dates (.releases) — mobile ---- */
  /* Reclaim width: the 26px/30px card padding is too generous at 360px. */
  .releases { padding: 18px 16px; }
  /* Premiere banner: the eyebrow + date fought for width side by side, so the
     uppercase "PREMIÈRE MONDIALE" wrapped. Stack them, each on its own line. */
  .releases-premiere {
    flex-direction: column; align-items: center; text-align: center;
    gap: 6px; padding: 14px 16px;
  }
  .premiere-date { font-size: 20px; }
  /* The Date / Région / Plateformes table can't fit ~290px, so the Platforms
     column spilled out of the card. Turn each release into a stacked block:
     flag + région + date on one line, platforms full-width underneath. */
  .releases-table, .releases-table tbody, .releases-table tr, .releases-table td { display: block; }
  .releases-table thead { display: none; }
  .releases-table tr {
    display: flex; flex-wrap: wrap; align-items: baseline;
    column-gap: 10px; padding: 12px 0;
    border-bottom: 1px solid var(--rule-soft);
  }
  .releases-table tr:last-child { border-bottom: 0; }
  .releases-table td { padding: 0; border: 0; }
  .rt-region { order: 1; }
  .rt-date { order: 2; margin-left: auto; }
  .rt-platforms { order: 3; flex-basis: 100%; margin-top: 4px; color: var(--ink-mid); }
}

@media (max-width: 480px) {
  .review-meta { row-gap: 4px; }
  .comment-count { min-height: 26px; }
  .review-header { padding: 16px 14px; }
  .thread { padding: 0 14px; }
}


/* ==========================================================================
   [SHOW_DASHBOARD] — dashboard (show_dashboard.html)
   3 + 2 panel layout: top row = Moyenne / Catalogue / Vus,
   bottom row = Évolution mensuelle + Activité récente.
   ========================================================================== */

.dash {
  max-width: 1600px; margin: 0 auto;
  padding: 0 56px 24px;
  display: flex; flex-direction: column; gap: 14px;
}

/* Page-wide modifier — aligns page-head with .dash's 1600px width. */
.page-head.wide { max-width: 1600px; }

.dash .row-top {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px;
}
.dash .row-bot {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
  /* `start` instead of the default `stretch` — the activity card keeps its
     natural height instead of growing when the graphs column gets taller. */
  align-items: start;
  /* Shared height for the two row-bot cards. Each graph card is locked to
     this height and each new graph stacks below at the same size ; the
     activity card-body's max-height is derived from it. */
  --dash-card-h: 637px;
}

/* ---------- Card (generic) ---------- */
.dash .card {
  display: flex; flex-direction: column;
  position: relative; overflow: hidden;
  min-width: 0;
}
.dash .card::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: var(--violet);
}
.dash .card.amber::before { background: var(--warn); }
.dash .card.teal::before  { background: var(--accent-teal); }
.dash .card.plain::before { display: none; }

.dash .card-head {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--rule);
}
/* Align the head/body divider across both cards of the bottom row.
   The graph head carries pills (32px) that make it taller than a head with
   only an H2, so we lock the activity head to the same minimum height. */
.dash .row-bot .card-head { min-height: 61px; }
.dash .card-head .num {
  font-family: var(--font-display);
  font-style: italic; font-weight: 300; font-size: 32px;
  line-height: 1; letter-spacing: -.03em;
  color: var(--violet); width: 42px;
}
.dash .card.amber .card-head .num { color: var(--warn); }
.dash .card.teal  .card-head .num { color: var(--accent-teal); }
.dash .card-head .titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dash .card-head .eyebrow {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-dim);
}
.dash .card-head h2 {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 20px;
  letter-spacing: -.02em; color: var(--ink); line-height: 1.05;
}
.dash .card-head .ctrls { margin-left: auto; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

.dash .card-body {
  flex: 1 1 auto;
  padding: 18px 22px;
  display: flex; flex-direction: column;
}
.dash .card-foot {
  border-top: 1px solid var(--rule);
  padding: 9px 18px 10px;
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 10px; color: var(--ink-dim);
}

/* ---------- Dash pill (scoped to avoid collision with .byline .pill-status) ---------- */
.dash .dash-pill {
  height: 32px; box-sizing: border-box;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-mid);
  background: var(--paper-card); border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0 14px;
  display: inline-flex; align-items: center; gap: 6px;
  line-height: 1;
  transition: all .2s; white-space: nowrap; cursor: pointer;
}
.dash .dash-pill:hover { border-color: var(--violet); color: var(--violet); }
.dash .dash-pill.solid { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.dash .dash-pill.solid:hover { background: var(--violet); border-color: var(--violet); color: var(--paper); }
.dash .dash-pill.year { padding: 0 6px; }
.dash .dash-pill.year button {
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; font-size: 12px;
  color: var(--ink-mid); background: transparent; border: 0; cursor: pointer;
}
.dash .dash-pill.year button:hover { color: var(--violet); }
.dash .dash-pill.year .yval {
  font-family: var(--font-display);
  font-weight: 500; font-style: italic; font-size: 14px;
  letter-spacing: -.01em; color: var(--ink);
  padding: 0 6px; text-transform: none;
}
.dash select.dash-pill {
  appearance: none; -webkit-appearance: none;
  /* The select auto-sizes to its content, leaving the longest pseudo flush
     against the dropdown arrow. A small floor gives the name breathing room,
     and centring sits it in the middle rather than trailing space to the right. */
  min-width: 104px;
  text-align: center;
  padding: 0 26px 0 14px;
  /* Mockup-style content typography: Bricolage italic, not uppercase Outfit. */
  font-family: var(--font-display);
  font-style: italic; font-weight: 500;
  font-size: 12px; letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-mid) 50%),
                    linear-gradient(135deg, var(--ink-mid) 50%, transparent 50%);
  background-position: calc(100% - 14px) 50%, calc(100% - 9px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* You-block — current user hero stat. Avatar background color comes from
   User.theme_color injected inline by the template — no static palette. */
.dash .you-block {
  display: flex; align-items: center; gap: 14px;
  padding: 2px 0 10px;
  border-bottom: 1px dashed var(--rule);
}
.dash .you-block .ava-big {
  position: relative; overflow: hidden;
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; color: #fefdfa;
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 19px;
  flex: 0 0 auto;
}
.dash .you-block .col { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.dash .you-block .me {
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 22px;
  letter-spacing: -.02em; color: var(--ink); line-height: 1.1;
}
.dash .you-block .big {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500;
  font-size: 42px; line-height: 1; letter-spacing: -.03em;
  color: var(--ink); margin-left: auto;
}
.dash .you-block .big sup {
  font-family: var(--font-ui); font-style: normal;
  font-weight: 900; font-size: 13px; color: var(--violet);
  letter-spacing: 0; margin-left: 3px; vertical-align: 22px;
}
/* .meta — compact JetBrains Mono inline counts (ciné · maison).
   Used identically by the current user (.you-block) and the others list. */
.dash .you-block .meta { margin-top: 4px; }

/* Catalogue panel — 3 stacked coverage stats on the left, "À voir"
   suggestion on the right (poster + title + director) separated by a
   vertical dashed hairline. */
.dash .cata-split {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 22px;
  height: 100%;
}
.dash .cata {
  display: flex; flex-direction: column;
  flex: 1 1 auto;
}
/* Suggestion — full-bleed poster cinema-card style on the right half.
   Breaks out of the card-body padding to reach the card edges; the meta
   sits on top with a vertical dark gradient overlay. */
.dash .cata-suggestion {
  position: relative;
  display: block;
  margin: -18px -22px -18px 0;
  background: var(--ink);
  overflow: hidden;
  min-height: 220px;
  transition: transform .25s ease, box-shadow .25s ease;
}
.dash .cata-suggestion:hover { transform: scale(1.01); }
.dash .cata-suggestion-poster {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform .6s ease;
}
.dash .cata-suggestion:hover .cata-suggestion-poster {
  transform: scale(1.04);
}
.dash .cata-suggestion-overlay {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 28px 18px 16px;
  background: linear-gradient(to top,
    rgba(28, 24, 32, .92) 0%,
    rgba(28, 24, 32, .65) 50%,
    rgba(28, 24, 32, 0) 100%);
  display: flex; flex-direction: column; gap: 4px;
  color: #fefdfa;
}
/* Solid badge pinned top-left so the label stays legible over any poster
   (it used to be a small amber caption on the bottom scrim and washed out
   on bright artwork). */
.dash .cata-suggestion-head {
  position: absolute; top: 12px; left: 12px; z-index: 1;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink);
  background: var(--warn);
  padding: 5px 11px 4px;
  border-radius: 999px;
  box-shadow: 0 3px 12px -3px rgba(28, 24, 32, .6);
}
.dash .cata-suggestion .title {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 22px;
  letter-spacing: -.02em; color: #fefdfa; line-height: 1.1;
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.dash .cata-suggestion .meta {
  font-family: var(--font-body); font-size: 11.5px;
  color: rgba(254, 253, 250, .75); line-height: 1.3;
  display: flex; align-items: baseline; gap: 5px; min-width: 0;
}
.dash .cata-suggestion .director {
  min-width: 0; flex: 0 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash .cata-suggestion .year {
  font-family: var(--font-mono);
  color: rgba(254, 253, 250, .6);
  flex: 0 0 auto; white-space: nowrap;
}
.dash .cata-stat {
  display: flex; flex-direction: column; align-items: center;
  gap: 6px;
  padding: 14px 0;
  border-bottom: 1px dashed var(--rule-soft);
  flex: 1 1 0;
  justify-content: center;
}
.dash .cata-stat:last-child { border-bottom: 0; }
.dash .cata-stat .num {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500;
  font-size: 40px; line-height: 1; letter-spacing: -.03em;
  color: var(--ink);
}
.dash .cata-stat .lbl {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-dim);
}
.dash .cata-stat .pct { color: var(--warn); }

/* Others (compared users list inside Moyenne / Vus) */
.dash .others-head {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 9px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-dim);
  padding: 10px 0 4px;
}
.dash .others {
  display: flex; flex-direction: column;
  /* Show exactly 3 rows, then scroll vertically for the rest. The scrollbar
     only appears when there are more than 3 others (a shorter list stays under
     max-height). --row-h is the uniform row height set on .row below. */
  --row-h: 65px;
  max-height: calc(var(--row-h) * 3);
  overflow-y: auto;
  padding-right: 14px;   /* gap between the value column and the scrollbar */
  scrollbar-width: thin;
}
.dash .others::-webkit-scrollbar { width: 6px; }
.dash .others::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 3px; }
.dash .others .row {
  display: grid; grid-template-columns: 34px 1fr auto;
  gap: 12px; align-items: center;
  padding: 12px 0;
  border-bottom: 1px dashed var(--rule-soft);
  /* Uniform height so "3 rows" is exact in every panel and show type — the Vus
     panel's ciné/maison meta line makes movie rows the tallest (65px). */
  min-height: var(--row-h); flex-shrink: 0;
}
.dash .others .row:last-child { border-bottom: 0; }
.dash .others .row .ava {
  position: relative; overflow: hidden;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; color: #fefdfa;
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 15px;
}
.dash .others .row .who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dash .others .row .name {
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 20px;
  letter-spacing: -.02em; color: var(--ink); line-height: 1.1;
}
.dash .you-block .meta {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-dim);
}
.dash .others .row .meta {
  font-family: var(--font-mono);
  font-size: 10.5px; color: var(--ink-dim);
}
.dash .others .row .v {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500;
  font-size: 24px; line-height: 1; letter-spacing: -.02em;
  color: var(--violet);
}

/* Graph stack — Chart.js v4 canvas styled to match the v5 mockup */
.dash .graph-stack { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.dash .graph-stack > .card { height: var(--dash-card-h); flex: 0 0 auto; }
.dash .graph-stack .card-body {
  padding: 14px 18px 10px; min-height: 380px;
  position: relative;
}
.dash .graph-stack canvas { width: 100% !important; height: 100% !important; min-height: 340px; }

.dash .legend {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-mid);
}
.dash .legend i { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.dash .legend i.cine { background: var(--violet); }
.dash .legend i.home { background: var(--accent-rose); opacity: .85; }
.dash .legend-count { color: var(--ink); margin-left: 2px; }

/* Activity feed — the [ACTIVITY_FLOW] timeline reused inside a dashboard card.
   The node / marker / spine / avatar classes are global (the activity table
   carries class="flux") ; only the white-card adaptations are scoped here. */
/* The gap between the timeline and the card frame is the card's OWN
   padding-bottom — it sits outside the scrollable .card-body, so it renders
   identically in every browser (a scroll container's own bottom padding is
   computed inconsistently between Chrome and Firefox). */
.dash .activity { padding-bottom: 14px; }
.dash .activity .card-body {
  padding: 4px 22px 8px;
  overflow-y: auto;
  /* Derived from --dash-card-h so the activity total height matches the
     graph card. 77px = card-head min-height (61) + card padding-bottom (14)
     + borders (2). */
  max-height: calc(var(--dash-card-h) - 77px);
}
/* on the white card the marker's halo blends with --paper-card, not --paper */
.dash .activity .act-marker { box-shadow: 0 0 0 4px var(--paper-card); }
.dash .activity .act-node:hover .act-marker {
  box-shadow: 0 0 0 4px var(--paper-card),
              0 0 0 var(--act-halo) color-mix(in srgb, currentColor 22%, transparent);
}
/* the hover lift reads against the white card via --paper-soft */
.dash .activity .act-node:hover .act-body { background: var(--paper-soft); }
/* a touch of space below the last node, inside the scroll area */
.dash .activity tbody tr:last-child .act-node { padding-bottom: 12px; }

.dash .display_button { font-size: 12px; line-height: 1; }

/* Latest rated shows — sits inside a standard .dash .card (white bg, violet
   accent strip on top, card-head with the section title, card-body with the
   grid). Each .latest-item borrows the wizard select-show language : poster
   on top, meta below ; the rater info (avatar + nickname + mark) replaces
   the wizard's director/year. */
.dash .latest-grid {
  display: grid;
  /* 10 compact cards spread across the card-body so the "Dernières notes"
     bandeau fills the dashboard width on a single line. */
  grid-template-columns: repeat(10, minmax(0, 130px));
  justify-content: space-between;
  gap: 12px;
}
@keyframes latest-reveal {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
.dash .latest-item {
  position: relative;
  /* Sub-cards inside the white outer .card need to read distinct. We use the
     page paper tone (slightly beige) + a softer rule, so they look inset.
     On hover they whiten and lift, telegraphing the click affordance. */
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  border-radius: var(--card-radius);
  overflow: hidden;
  text-decoration: none; color: inherit;
  display: block;
  transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease, background .25s ease;
  animation: latest-reveal .6s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: calc(var(--i, 0) * 60ms);
}
@media (prefers-reduced-motion: reduce) {
  .dash .latest-item { animation: none; }
}
.dash .latest-item:hover {
  transform: translateY(-4px);
  background: var(--paper-card);
  border-color: var(--ink);
  box-shadow: 0 18px 32px -16px rgba(28, 24, 32, .22);
}
.dash .latest-poster {
  position: relative;
  aspect-ratio: 2 / 3;
  overflow: hidden;
}
.dash .latest-poster .poster-placeholder {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background:
    radial-gradient(circle at 30% 30%, rgba(var(--violet-rgb),.35), transparent 60%),
    var(--ink);
  color: var(--violet);
}
.dash .latest-poster .poster-placeholder i {
  font-size: 48px; opacity: .35; transform: rotate(-8deg);
}
.dash .latest-poster img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  z-index: 1;
}
/* Title overlaid on the poster, same language as .cata-suggestion-overlay:
   dark gradient anchored at the bottom, light italic display text. */
.dash .latest-overlay {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 22px 10px 10px;
  background: linear-gradient(to top,
    rgba(28, 24, 32, .92) 0%,
    rgba(28, 24, 32, .65) 45%,
    rgba(28, 24, 32, 0) 100%);
  z-index: 2;
}
.dash .latest-overlay .title {
  display: -webkit-box;
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 13px;
  line-height: 1.15; letter-spacing: -.01em;
  color: #fefdfa;
  overflow: hidden; text-overflow: ellipsis;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
/* The rater's mark, promoted to a pill anchored in the poster's top-right
   corner (tinted with the rater's theme_color). Frees the whole meta row
   below for the full nickname, however long. */
.dash .latest-poster .latest-badge {
  position: absolute; top: 8px; right: 8px; z-index: 3;
  padding: 3px 9px; border-radius: 999px;
  font-family: var(--font-ui); font-weight: 800; font-size: 13px;
  font-feature-settings: "tnum" 1;
  line-height: 1.35; color: var(--paper-card);
  box-shadow: 0 2px 8px -2px rgba(28, 24, 32, .5);
}
/* Meta below the poster : a single centered row with avatar + full nickname. */
.dash .latest-meta {
  padding: 10px 12px 12px;
}
.dash .latest-meta .rater {
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
.dash .latest-meta .rater .ava {
  position: relative; overflow: hidden;
  width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--paper-card);
  font-family: var(--font-ui); font-weight: 900; font-size: 10px;
  flex: 0 0 auto;
}
.dash .latest-meta .rater .name {
  font-family: var(--font-body); font-size: 12.5px;
  color: var(--ink-mid); font-style: italic;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Responsive — ≤1100px */
@media (max-width: 1100px) {
  .dash .row-top { grid-template-columns: 1fr; }
  .dash .row-bot { grid-template-columns: 1fr; }
}
@media (max-width: 920px) {
  .dash { padding: 0 20px 32px; }
  .dash .card-head { flex-wrap: wrap; gap: 8px; }
  .dash .card-head .ctrls { width: 100%; justify-content: flex-start; }

  /* Graph card controls — the three pills (user filter, year nav, "+ Graphique")
     don't fit on one 360px row, so the action pill dangled alone on a second
     line. Lay them out as a grid: user filter + year nav share the top row at
     equal width, and the "+ Graphique" / "− Retirer" trigger (kept as
     .dash-pill.solid on cloned graphs too) spans the full width below. */
  .dash .graph-stack .card-head .ctrls {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  }
  .dash .graph-stack .card-head .ctrls > * { justify-content: center; }
  .dash .graph-stack .card-head .ctrls .dash-pill.solid { grid-column: 1 / -1; }

  /* Latest-rated turns into a horizontal carousel — 10 cards in one row at
     readable width, with native horizontal scroll. The card-body negative
     padding lets the carousel touch the card edges so swipes feel natural. */
  .dash .latest-rated .card-body { padding: 14px 0 16px; }
  .dash .latest-grid {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 150px;
    justify-content: start;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding: 0 18px 8px;
    /* Mandatory snap + scroll-snap-align:start pulls the first card flush to
       the scrollport edge, eating the leading 18px padding so it sat too far
       left of the card title. scroll-padding-left makes the snap respect the
       18px inset, aligning the first card with the "Dernières notes" heading. */
    scroll-padding-left: 18px;
    -webkit-overflow-scrolling: touch;
  }
  .dash .latest-item { scroll-snap-align: start; }

  /* Cata card ("La cinémathèque/ludothèque/sériethèque") — a 50/50 split leaves
     the stats block ~130px, too narrow for the longest label ("TERMINÉS PAR LE
     GROUPE · 38 %") which then wraps. Stack on mobile: the numbers/labels take
     the full card width (labels hold one line at their normal size) and the
     suggestion poster becomes a full-bleed banner along the bottom edge. */
  .dash .cata-split { grid-template-columns: 1fr; }
  .dash .cata-suggestion { margin: 4px -22px -18px; min-height: 180px; }
}


/* ==========================================================================
   [WIZARD] — add / select / confirm show wizard (*_show_wizard.html)
   ========================================================================== */

.wizard-stage {
  max-width: 880px; margin: 0 auto;
  padding: 0 24px 80px;
  text-align: center;
  position: relative;
}

/* Select-step result-page indicator (« Sélection · Page X sur Y »).
   Caption voice : Outfit 900 11px .14em uppercase ink-dim ; centered by .wizard-stage. */
.wizard-page-indicator {
  font-size: 11px;
  margin-bottom: 22px;
}

.wizard-lead {
  font-family: var(--font-body);
  font-size: 18px; line-height: 1.5;
  color: var(--ink-mid);
  max-width: 540px; margin: 0 auto 48px;
}

/* Big search — typographic input, no box, just an underline */
.wizard-search {
  margin: 0 auto 24px; max-width: 680px;
}
.wizard-search-input {
  width: 100%;
  background: transparent; border: 0; outline: 0;
  border-bottom: 2px solid var(--ink);
  padding: 14px 4px;
  font-family: var(--font-display);
  font-style: italic; font-weight: 400;
  font-size: clamp(28px, 4vw, 42px); line-height: 1.1;
  letter-spacing: -.025em;
  color: var(--ink);
  text-align: center;
  transition: border-color .25s;
}
.wizard-search-input::placeholder {
  color: var(--ink-dim); font-style: italic;
}
.wizard-search-input:focus { border-bottom-color: var(--violet); }

.wizard-hint {
  font-size: 11px;
  margin-bottom: 36px;
}
.wizard-hint .accent { color: var(--violet); font-weight: 600; }
.wizard-hint kbd {
  display: inline-flex; align-items: center;
  padding: 5px 12px;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--paper-card); color: var(--ink-mid);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  line-height: 1;
  margin: 0 4px; vertical-align: middle;
}

.wizard-submit-row {
  display: flex; justify-content: center; gap: 14px; flex-wrap: wrap;
}
/* Wizard CTA — the actual visual (gradient + shadow + hover-lift) lives on
   the canonical `.btn-solid`. Wizard buttons only add the trailing arrow
   slide animation on top. */
.wizard-submit .arrow { transition: transform .2s; display: inline-block; margin-left: 4px; }
.wizard-submit:hover .arrow { transform: translateX(4px); }

/* Overlay spinner (preserved from legacy) */
#container_overlay {
  display: none;
  position: fixed; inset: 0;
  background: rgba(246, 243, 240, .8);
  backdrop-filter: blur(4px);
  z-index: 9999;
}
#container_overlay #spinner {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 56px; height: 56px;
  border: 3px solid var(--rule);
  border-top-color: var(--violet);
  border-radius: 50%;
  animation: wizard-spin .9s linear infinite;
}
@keyframes wizard-spin {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}

@media (max-width: 760px) {
  .wizard-stage { padding: 0 18px 60px; }
  .wizard-search-input { font-size: 28px; padding: 12px 4px; }
}


/* ============== WIZARD step 2 — select : grid of result cards + pager ============== */

.wizard-stage-wide { max-width: 1280px; }

/* Grid of result cards */
.wizard-results {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 22px;
  margin: 40px 0;
  text-align: left;
}
@keyframes wizard-reveal {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: translateY(0); }
}
.wizard-result {
  position: relative;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--card-radius);
  overflow: hidden;
  cursor: pointer;
  transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease;
  animation: wizard-reveal .8s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: calc(var(--i, 0) * 80ms);
}
@media (prefers-reduced-motion: reduce) {
  .wizard-result { animation: none; }
}
.wizard-result:hover {
  transform: translateY(-4px);
  border-color: var(--ink);
  box-shadow: 0 18px 32px -16px rgba(28, 24, 32, .22);
}
.wizard-result input[type="radio"] {
  position: absolute; opacity: 0; pointer-events: none;
}
.wizard-result:has(input[type="radio"]:checked) {
  border: 2px solid var(--violet);
  box-shadow: 0 0 0 6px rgba(var(--violet-rgb),.12),
              0 24px 40px -18px rgba(var(--violet-rgb),.4);
  transform: translateY(-4px);
}

/* Poster — re-uses .poster-placeholder language, but no rotation/hatching
   (override the global .poster rules from display_show). */
.wizard-result .poster {
  position: relative;
  display: block;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  transform: none;
  box-shadow: none;
  transition: none;
}
.wizard-result .poster:hover { transform: none; }
.wizard-result .poster::after { content: none; }
.wizard-result .poster .poster-placeholder {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background:
    radial-gradient(circle at 30% 30%, rgba(var(--violet-rgb),.35), transparent 60%),
    var(--ink);
  color: var(--violet);
}
.wizard-result .poster .poster-placeholder i {
  font-size: 56px; opacity: .35; transform: rotate(-8deg);
}
.wizard-result .poster img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  z-index: 1;
}

.wizard-result .meta {
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 16px 16px;
}
.wizard-result .meta .title {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 18px;
  line-height: 1.15; letter-spacing: -.02em;
  color: var(--ink);
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  /* Always reserve 2 lines so the publisher/year below sit at the same
     position whether the title wraps to 1 or 2 lines. */
  min-height: calc(1.15em * 2);
}
.wizard-result .meta .director {
  font-family: var(--font-body); font-size: 13px;
  color: var(--ink-mid);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Release year stamped onto the poster corner — like a date stamp on a game
   box. Light "sticker" so it stays legible over any cover or the dark
   placeholder; turns violet when the card is selected. */
.wizard-result .year-stamp {
  position: absolute;
  right: 10px; bottom: 10px; z-index: 3;
  padding: 5px 10px;
  font-family: var(--font-display);
  font-style: italic; font-weight: 700;
  font-size: 15px; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background: var(--paper-card);
  border-radius: 7px;
  box-shadow: 0 4px 12px -4px rgba(28, 24, 32, .45);
  transition: background-color .25s ease, color .25s ease;
}
.wizard-result:has(input[type="radio"]:checked) .year-stamp {
  background: var(--violet); color: var(--paper);
}
@media (prefers-reduced-motion: reduce) {
  .wizard-result .year-stamp { transition: none; }
}
.wizard-result .meta .platforms {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 6px;
  font-family: var(--font-body); font-size: 11px; line-height: 1.35;
  color: var(--ink-dim);
}
.wizard-result .meta .platforms i {
  margin-right: 6px;
  opacity: .6;
}

/* Check mark on selected */
.wizard-result .check {
  position: absolute; top: 10px; right: 10px;
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--violet); color: #fff;
  font-size: 14px;
  opacity: 0; transform: scale(.7);
  transition: all .2s ease;
  z-index: 2;
  box-shadow: 0 4px 12px rgba(var(--violet-rgb),.4);
}
.wizard-result:has(input[type="radio"]:checked) .check {
  opacity: 1; transform: scale(1);
}

/* Actions bar : pager left + submit right (centered together) */
.wizard-actions {
  display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap;
  padding-top: 28px;
  border-top: 1px dashed var(--rule-soft);
}
/* Pager — same language as DataTables (.dt-paging .dt-paging-button). */
.wizard-pager {
  display: inline-flex; flex-wrap: wrap; gap: 4px;
  margin: 0; padding: 0; list-style: none;
  font-family: var(--font-ui);
}
.wizard-pager-link {
  display: inline-grid; place-items: center;
  min-width: 32px; height: 32px; padding: 0 12px;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink-mid);
  font-family: var(--font-mono);
  font-size: 12px; letter-spacing: 0;
  text-transform: none; font-weight: 500;
  cursor: pointer; transition: all .15s;
  text-decoration: none;
}
.wizard-pager-link.previous,
.wizard-pager-link.next {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase;
  padding: 0 16px;
}
.wizard-pager-link:hover {
  color: var(--violet);
  border-color: var(--violet);
}
.wizard-pager-link.is-active,
.wizard-pager-link.is-active:hover {
  background: var(--violet);
  border-color: var(--violet);
  color: var(--paper-card);
}
.wizard-pager-link.is-disabled,
.wizard-pager-link.is-disabled:hover {
  opacity: .35; cursor: not-allowed;
  border-color: transparent;
  color: var(--ink-dim);
  background: transparent;
}

@media (max-width: 760px) {
  .wizard-results { gap: 14px; }
  .wizard-actions { justify-content: center; gap: 16px; }
}

/* ============== WIZARD step 3 — confirm : reuses .show-detail hero ============== */

/* The hero reuses .hero.show-detail from display_show. We only add some
   horizontal centering on the wizard-stage-wide width. */
.wizard-stage.wizard-stage-wide .hero.show-detail {
  text-align: left;
  margin: 0 auto 40px;
}

/* Confirm form : type + origin */
.wizard-confirm-meta {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 18px;
  max-width: 720px;
  margin: 0 auto 28px;
  text-align: left;
}
.wizard-confirm-meta .field { display: flex; flex-direction: column; gap: 8px; }
.wizard-confirm-meta label {
  font-size: 11px;
}
.wizard-confirm-meta select.confirm-select {
  height: 48px; padding: 0 36px 0 16px;
  appearance: none; -webkit-appearance: none;
  background: var(--paper-card);
  border: 1px solid var(--rule); border-radius: 10px;
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 16px;
  color: var(--ink); outline: none;
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-mid) 50%),
    linear-gradient(135deg, var(--ink-mid) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  transition: border-color .2s, box-shadow .2s;
}
.wizard-confirm-meta select.confirm-select:focus {
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(var(--violet-rgb),.12);
}

@media (max-width: 760px) {
  .wizard-confirm-meta { grid-template-columns: 1fr; }
}

/* ==========================================================================
   [HOMEWORKS] — homework list (list_homeworks.html)
   A homework row reads as a directional handoff: giver ──▶ receiver.
   Per-user avatar colors (.hw-ava.user-<id>) are emitted inline by Jinja.
   ========================================================================== */
.hw-wrap { max-width: 1340px; margin: 0 auto; padding: 8px 56px 80px; }

/* ----- stacked sections : "À voir" on top, "Notés" below ----- */
.hw-columns {
  display: flex; flex-direction: column;
  gap: 44px;
}
.hw-col { min-width: 0; }
.hw-col-empty {
  font-family: var(--font-body); font-style: italic;
  font-size: 14px; color: var(--ink-dim);
  padding: 20px 4px;
  display: flex; align-items: center; gap: 8px;
}
.hw-col-empty i { font-size: 16px; opacity: .7; }

/* ----- section bands : "À voir" / "Notés" ----- */
.hw-section-head {
  display: flex; align-items: center; gap: 16px;
  margin: 0 0 18px;
}
.hw-section-title {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink); white-space: nowrap;
}
.hw-section-title .accent { color: var(--violet); }
.hw-section-rule { flex: 1; border-top: 1px dashed var(--rule); }
.hw-section-count {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .05em; color: var(--ink-dim);
  font-feature-settings: "tnum" 1;
}

/* ----- list + row ----- */
.hw-list { display: flex; flex-direction: column; gap: 14px; }
.hw-row {
  position: relative;
  display: grid;
  /* 56 / 1fr / 172 mirrors the display_show .review-header grid so the
     score column lands in the exact same slot on both pages. */
  grid-template-columns: 56px 1fr 172px;
  gap: 22px;
  align-items: center;
  padding: 20px 24px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: var(--card-radius);
  transition: border-color .2s ease, box-shadow .2s ease;
}
.hw-row:hover {
  border-color: var(--ink);
  box-shadow: 0 14px 30px -18px rgba(28, 24, 32, .22);
}
.hw-row.is-mine {
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(var(--violet-rgb),.08);
}
.hw-row.is-mine:hover {
  box-shadow: 0 0 0 3px rgba(var(--violet-rgb),.12),
              0 14px 30px -18px rgba(var(--violet-rgb),.32);
}

/* ----- poster ----- */
.hw-poster {
  display: block; position: relative;
  width: 56px; aspect-ratio: 2 / 3;
  border: 1px solid var(--rule); border-radius: 5px;
  overflow: hidden; background: var(--paper-soft);
}
.hw-poster img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; z-index: 1;
}

/* ----- body : title + director + transfer ----- */
.hw-body { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* gap matches .cell-title .ttx on the list page */
.hw-head { display: flex; flex-direction: column; gap: 1px; }
.hw-title {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500;
  font-size: 22px; line-height: 1.12; letter-spacing: -.02em;
  color: var(--ink); text-decoration: none;
  transition: color .15s;
}
.hw-title:hover { color: var(--violet); }
.hw-director {
  font-family: var(--font-body); font-size: 13px;
  color: var(--ink-mid);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ----- the handoff : giver ──▶ receiver ----- */
.hw-transfer { display: flex; align-items: center; gap: 14px; }
.hw-party { display: inline-flex; align-items: center; gap: 8px; flex-shrink: 0; }
.hw-ava {
  position: relative; flex-shrink: 0;
  width: 30px; height: 30px; border-radius: 50%;
  display: inline-grid; place-items: center;
  overflow: hidden;
  background: var(--ink-dim);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 12px; color: #fefdfa;
}
.hw-ava img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.hw-name {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-mid);
}
.hw-arrow {
  flex: 1; min-width: 30px; height: 0;
  border-top: 1px solid var(--rule);
  position: relative;
}
.hw-arrow::after {
  content: ""; position: absolute; right: 0; top: 50%;
  width: 6px; height: 6px;
  border-top: 1.5px solid var(--ink-dim);
  border-right: 1.5px solid var(--ink-dim);
  transform: translate(1px, -50%) rotate(45deg);
}

/* ----- status : Noter / En cours / mark ----- */
/* Score / action column — mirrors the .score-block pattern from the
   show-detail review card: thin rule on the left, content centered, fills
   the full row height for a clean vertical separator. */
.hw-status {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 8px;
  border-left: 1px solid var(--rule);
  padding-left: 28px;
  align-self: stretch;
}
.hw-status .btn { width: 100%; justify-content: center; }
.hw-pending {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: #8a6500;
  padding: 9px 16px;
  border: 1px dashed #d49a00; border-radius: 999px;
  background: rgba(255, 200, 0, .10);
}
/* Identical typography to display_show .score so the score lands in the
   same visual slot whether you are reading a review or a homework. */
.hw-mark {
  font-family: var(--font-ui); font-weight: 600;
  font-size: 56px; line-height: 1; letter-spacing: -.015em;
  color: var(--violet); font-feature-settings: "tnum" 1;
}

/* ----- cancel cross (only for the giver, while unrated) ----- */
.hw-cancel {
  position: absolute; top: 12px; right: 14px;
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--rule); background: var(--paper);
  color: var(--ink-dim); font: inherit; font-size: 11px;
  text-decoration: none; cursor: pointer;
  -webkit-appearance: none; appearance: none;
  transition: border-color .15s, color .15s, background .15s;
}
.hw-cancel:hover {
  border-color: var(--error); color: var(--error);
  background: var(--paper-card);
}

/* ----- empty state ----- */
.hw-empty {
  display: flex; flex-direction: column; align-items: center; gap: 18px;
  padding: 90px 20px;
  text-align: center;
}
.hw-empty .hw-empty-illu {
  width: 160px; height: 120px;
  color: var(--ink-dim); opacity: .55;
}
.hw-empty p {
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 24px;
  color: var(--ink-mid);
}

@media (max-width: 720px) {
  .hw-wrap { padding: 8px 18px 60px; }
  /* Mirror the mobile .review-header layout (display_show reviews) :
     poster / body / score-block on a single row. The 3rd column has a
     fixed width so the separator always lands at the same X regardless
     of what the slot holds ("13" / "15.5" / "Noter →" / "En cours"). */
  .hw-row {
    grid-template-columns: 48px 1fr 96px;
    gap: 12px 14px;
    padding: 16px;
  }
  .hw-poster { width: 48px; }
  .hw-title { font-size: 19px; }
  /* The narrow middle column can't hold "giver → receiver" on one line, so the
     receiver used to wrap under a right-pointing arrow that pointed at nothing.
     Stack the transfer vertically instead: giver on top, receiver below, joined
     by a short vertical connector + downward chevron centred under the avatar,
     which reads as a clear top-to-bottom hand-off. */
  .hw-transfer { flex-direction: column; align-items: flex-start; gap: 3px; }
  .hw-arrow {
    flex: none;
    width: 0; min-width: 0; height: 13px;
    border-top: 0;
    border-left: 1px solid var(--rule);
    margin-left: 14.5px;
  }
  .hw-arrow::after {
    right: auto; left: 50%; top: auto; bottom: -1px;
    width: 5px; height: 5px;
    border: 0;
    border-bottom: 1.5px solid var(--ink-dim);
    border-left: 1.5px solid var(--ink-dim);
    transform: translate(-50%, 0) rotate(-45deg);
  }
  /* Compact score column (matches .score-block mobile rules). */
  .hw-status {
    padding-left: 14px;
    gap: 2px;
  }
  /* Buttons span the column so the separator stays at a fixed X. */
  .hw-status .btn { width: 100%; padding: 7px 8px; }
  .hw-mark { font-size: 32px; }
  .hw-pending { padding: 6px 10px; font-size: 10px; }
  .hw-cancel { top: 10px; right: 10px; }
}

/* ==========================================================================
   [ACTIVITY_FLOW] — full activity feed (show_activity_flow.html)
   A DataTables-driven vertical timeline : each <tr> is one event node, the
   type pill is the marker seated on the dorsal spine, the user avatar is
   inline at the head of the phrase. The pager (.dt-info / .dt-paging) reuses
   the global styles declared in the [LIST_SHOWS] section.
   ========================================================================== */
.flux-wrap { max-width: 1180px; margin: 0 auto; padding: 0 56px 20px; }

.flux-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  padding-bottom: 16px; margin-bottom: 28px;
  border-bottom: 1px solid var(--rule);
}
.flux-count {
  font-size: 11px;
}
.flux-count b {
  color: var(--violet); font-weight: 900;
  font-feature-settings: "tnum" 1; margin-right: 3px;
}

/* ----- the table behaves as a continuous timeline ----- */
table.flux { width: 100%; border-collapse: collapse; }
table.flux thead { display: none; }
table.flux tbody tr,
table.flux td.act-cell { background: transparent; }
/* !important to override DataTables' CDN CSS, which adds a `border-bottom`
   on every cell via `table.dataTable > tbody > tr > *` and would leave a
   stray hairline at the bottom of the last activity entry. */
table.flux td.act-cell { padding: 0; border: 0 !important; vertical-align: top; }

/* ----- one event node ----- */
.act-node {
  /* marker width + hover-halo spread are fluid : the media queries below
     shrink them as the viewport narrows so the pill never collides with the
     text block. The marker column is the pill width + 4px of slack. */
  --act-mk-w: 112px;
  --act-halo: 8px;
  position: relative;
  display: grid; grid-template-columns: calc(var(--act-mk-w) + 4px) 1fr; gap: 30px;
  padding: 9px 0;
}
/* the dorsal spine — each node draws its own 2px segment at the centre of the
   marker column ; the segments stack into one continuous line, clipped
   at the first and last marker so it never pokes past the timeline */
.act-node::before {
  content: ""; position: absolute;
  left: calc((var(--act-mk-w) + 4px) / 2 - 1px); top: 0; bottom: 0; width: 2px;
  background: var(--rule);
}
table.flux tbody tr:first-child .act-node::before { top: 24px; }
table.flux tbody tr:last-child  .act-node::before { bottom: calc(100% - 24px); }

/* ----- type pill = timeline marker, opaque so it masks the spine ----- */
.act-marker {
  align-self: start; justify-self: center;
  position: relative; margin-top: 3px;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--act-mk-w); padding: 5px 6px; border-radius: 999px;
  background: var(--paper-card);
  border: 1px solid currentColor;
  box-shadow: 0 0 0 4px var(--paper);
  font-family: var(--font-ui); font-weight: 900;
  font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase;
  white-space: nowrap;
  transition: box-shadow .2s ease, transform .2s ease;
}
.k-entree      { color: var(--accent-sage); }
.k-note        { color: var(--violet); }
.k-devoir      { color: var(--warn); }
.k-commentaire { color: var(--accent-teal); }
.k-favori      { color: var(--accent-rose); }
.act-node:hover .act-marker {
  transform: scale(1.05);
  box-shadow: 0 0 0 4px var(--paper),
              0 0 0 var(--act-halo) color-mix(in srgb, currentColor 22%, transparent);
}

/* ----- event body ----- */
.act-body {
  padding: 6px 16px 10px; margin: -6px -16px -10px;
  border-radius: 14px;
  transition: background .2s ease, box-shadow .2s ease;
}
.act-node:hover .act-body {
  background: var(--paper-card);
  box-shadow: 0 18px 38px -26px rgba(28, 24, 32, .42);
}
.act-head { display: flex; align-items: baseline; gap: 24px; }
.act-phrase {
  flex: 1; min-width: 0;
  font-family: var(--font-body);
  font-size: 15px; line-height: 1.55; color: var(--ink-mid);
}
.act-when {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 10.5px; letter-spacing: .03em; color: var(--ink-dim);
  white-space: nowrap;
}
/* user avatar — inline marker of the actor, coloured by User.theme_color */
.act-av {
  position: relative; overflow: hidden;
  display: inline-grid; place-items: center;
  vertical-align: middle; margin-right: 8px;
  width: 22px; height: 22px; border-radius: 50%;
  color: #fefdfa;
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 11px;
}
.act-phrase strong {
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 16px;
  letter-spacing: -.01em; color: var(--ink);
}
.act-phrase a {
  color: var(--violet); font-weight: 500;
  border-bottom: 1px solid transparent; transition: border-color .15s;
}
.act-phrase a:hover { border-bottom-color: var(--violet); }
.act-phrase .mark {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 13px; color: var(--violet);
  padding: 1px 8px; border-radius: 999px;
  background: var(--violet-tint);
  font-feature-settings: "tnum" 1;
}
.act-phrase .star { color: var(--warn); }

/* ----- quoted review (marks) / sub-comment (comments) ----- */
.act-quote {
  position: relative; max-width: 760px;
  margin-top: 11px; padding: 13px 17px 13px 40px;
  background: var(--paper-soft); border-radius: 12px;
  font-family: var(--font-body); font-style: italic;
  font-size: 13.5px; line-height: 1.55; color: var(--ink-mid);
}
.act-quote::before {
  content: "\201C";
  position: absolute; left: 13px; top: 9px;
  font-family: var(--font-display);
  font-size: 40px; line-height: 1; color: var(--violet-soft);
}
.act-quote p { margin: 0 0 .5em; }
.act-quote p:last-child { margin-bottom: 0; }

/* the activity feed is a timeline, not a carded table — opt out of the
   global .dt-layout-table card declared in [LIST_SHOWS] */
#activitylist_wrapper .dt-layout-row.dt-layout-table {
  background: transparent; border: 0; border-radius: 0;
  box-shadow: none; margin: 0; overflow: visible;
}

/* ----- pager — the bottom info + paging row ; .dt-info / .dt-paging styles
   are global (declared in [LIST_SHOWS]). The page-length selector is not here :
   it lives in .flux-head (see the template). ----- */
/* The :has(.dt-paging) qualifier scopes this to the full activity flow
   page (which has pagination) and leaves the dashboard's activity widget
   untouched (paging:false → no .dt-paging element). */
#activitylist_wrapper .dt-layout-row:has(.dt-paging) {
  margin: 18px 0 0; padding-top: 18px;
  border-top: 1px solid var(--rule);
}

@media (max-width: 920px) {
  .flux-wrap { padding: 0 20px 16px; }
  .flux-head { flex-wrap: wrap; gap: 10px; }
  /* shrink the pill as the viewport narrows so its hover halo never reaches
     the text block (the larger desktop pill would collide at this width) */
  .act-node { --act-mk-w: 84px; --act-halo: 4px; gap: 18px; }
  .act-marker { font-size: 8px; letter-spacing: .08em; padding: 4px 5px; margin-top: 2px; }
  .act-body { padding-left: 10px; margin-left: -10px; }
  .act-head { gap: 14px; }
}

/* ==========================================================================
   [SHOW_GRAPHS] — stats graphs (show_graphs.html)
   One graph per page ; a navigator over GRAPH_LIST jumps to any graph.
   Per-user colours are emitted inline by the template (current user violet,
   others cycle the accent palette).
   ========================================================================== */

/* ----- quick navigator over the 10 graphs ----- */
.graph-nav-wrap { max-width: 1480px; margin: 0 auto; padding: 0 56px 26px; }
.graph-nav-label {
  font-size: 11px;
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  gap: 14px;
}
.graph-nav-label::after { content: ""; flex: 1; height: 1px; background: var(--rule); }
/* the 10 graphs as a uniform grid — every cell the same width, labels wrap
   in full (no truncation, no horizontal scroll at any width) */
.graph-nav { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.gpill {
  /* Uniform pill height + vertically centred content so labels that wrap to a
     different number of lines (1–3 depending on breakpoint) stay aligned in a
     regular grid instead of top-aligned with ragged empty space below. The
     min-height fits the 2-line worst case here; the 2-column breakpoint below
     bumps it for the 3-line worst case. */
  display: flex; align-items: center; gap: 10px;
  min-height: 60px;
  padding: 13px 15px;
  background: var(--paper-card); border: 1px solid var(--rule);
  border-radius: 12px;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .07em; text-transform: uppercase;
  line-height: 1.35; color: var(--ink-mid);
  transition: border-color .18s, color .18s, background .18s;
}
.gpill:hover { border-color: var(--violet); color: var(--violet); }
.gpill .num {
  flex-shrink: 0;
  font-family: var(--font-mono); font-weight: 500;
  font-size: 10px; letter-spacing: .04em; color: var(--ink-dim);
  text-transform: none;
}
.gpill .gpill-label { flex: 1; }
.gpill.is-active { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.gpill.is-active .num { color: rgba(255, 255, 255, .5); }

/* ----- graph card ----- */
.graph-wrap { max-width: 1480px; margin: 0 auto; padding: 0 56px 32px; }
.graph-card {
  overflow: hidden;
}
.graph-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 14px;
  padding: 16px 30px;
  border-bottom: 1px solid var(--rule-soft);
}

/* legend — one entry per user, click to mute the curve */
.graph-legend { display: flex; flex-wrap: wrap; gap: 6px 24px; }
.glegend { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; }
.glegend .lava {
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; flex-shrink: 0;
  color: #fefdfa;
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 12px;
}
.glegend .lname {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
}
.glegend.is-muted { opacity: .34; }

/* mode toggle — line graphs only */
.graph-mode {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px; background: var(--paper-soft); border-radius: 999px;
}
.graph-mode button {
  padding: 6px 14px; border: 0; border-radius: 999px;
  background: transparent;
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-mid); cursor: pointer;
  transition: background .15s, color .15s;
}
.graph-mode button:hover { color: var(--violet); }
.graph-mode button.is-active {
  background: var(--paper-card); color: var(--ink);
  box-shadow: 0 1px 2px rgba(28, 24, 32, .1);
}

/* canvas */
.graph-body { padding: 22px 28px 26px; }
.canvas-wrap { position: relative; height: 440px; }
.canvas-wrap canvas { width: 100% !important; height: 100% !important; }

@media (max-width: 1100px) {
  .graph-nav { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 920px) {
  .graph-nav-wrap, .graph-wrap { padding-left: 20px; padding-right: 20px; }
  .graph-toolbar { padding: 14px 18px; }
  .graph-body { padding: 16px 12px 20px; }
  .canvas-wrap { height: 320px; }
}
@media (max-width: 620px) {
  .graph-nav { grid-template-columns: repeat(2, 1fr); }
  /* Two narrow columns push the longest labels to 3 lines — grow the uniform
     pill height to match so every button stays the same size. */
  .gpill { min-height: 74px; }
}

/* ==========================================================================
   [CHAT] — the Trollbox (chat.html)
   One global real-time room. Messages are grouped by author and split by day
   client-side ; your own messages sit right (violet), the others left.
   ========================================================================== */
.chat-wrap { max-width: 1480px; margin: 0 auto; padding: 36px 56px 28px; }
.chat-card {
  position: relative;
  display: flex; flex-direction: column;
  /* fill the viewport minus the chrome (topbar + wrap padding + footer) so the
     whole page fits a 1080-tall viewport, footer included, without scrolling ;
     capped on tall screens */
  height: min(calc(100vh - 256px), 860px);
  overflow: hidden;
  box-shadow: 0 30px 70px -46px rgba(28, 24, 32, .45);
}
/* top accent liseré — same cue as the dashboard cards */
.chat-card::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: var(--violet);
}

/* ----- white header band — title in the dashboard card-head type ----- */
.chat-head {
  padding: 15px 24px;
  border-bottom: 1px solid var(--rule);
  font-family: var(--font-display);
  font-style: italic; font-weight: 500; font-size: 20px;
  letter-spacing: -.02em; line-height: 1.05; color: var(--ink);
}

/* ----- message stream ----- */
.chat-stream {
  flex: 1; overflow-y: auto;
  background: var(--paper);
  padding: 22px 30px 8px;
  display: flex; flex-direction: column; gap: 4px;
}
.chat-empty {
  margin: auto;
  font-family: var(--font-body); font-style: italic;
  font-size: 14px; color: var(--ink-dim);
}

/* day divider */
.day-divider {
  display: flex; align-items: center; gap: 16px;
  margin: 14px 4px 16px;
}
.day-divider::before, .day-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--rule);
}
.day-divider span {
  font-family: var(--font-ui); font-weight: 900;
  font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-dim);
}

/* ----- one message group (consecutive messages from one author) ----- */
.msg-group {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 7px 0;
  opacity: 0; transform: translateY(10px);
  animation: msg-rise .5s cubic-bezier(.2, .7, .2, 1) forwards;
}
.msg-avatar {
  flex-shrink: 0;
  width: 38px; height: 38px; font-size: 15px;
}
.msg-stack { display: flex; flex-direction: column; gap: 4px; min-width: 0; max-width: 560px; }
.msg-meta { display: flex; align-items: baseline; gap: 9px; margin: 0 2px 1px; }
.msg-name {
  font-family: var(--font-display);
  font-style: italic; font-weight: 600; font-size: 14px;
  letter-spacing: -.01em;
}
.msg-time {
  font-family: var(--font-mono);
  font-size: 10px; color: var(--ink-dim); letter-spacing: .02em;
}

/* bubble — other users */
.msg-bubble {
  align-self: flex-start;
  max-width: 100%;
  padding: 9px 14px;
  background: var(--paper-card);
  border: 1px solid var(--rule);
  border-radius: 4px 15px 15px 15px;
  font-family: var(--font-body);
  font-size: 14.5px; line-height: 1.5; color: var(--ink);
  word-wrap: break-word;
}
.msg-bubble + .msg-bubble { border-radius: 15px; }

/* your own messages — right-aligned, violet */
.msg-group.is-you { flex-direction: row-reverse; }
.msg-group.is-you .msg-stack { align-items: flex-end; }
.msg-group.is-you .msg-meta { margin-right: 2px; }
.msg-group.is-you .msg-bubble {
  align-self: flex-end;
  background: var(--violet); border-color: var(--violet);
  color: #fdfcfa;
  border-radius: 15px 4px 15px 15px;
}
.msg-group.is-you .msg-bubble + .msg-bubble { border-radius: 15px; }

/* @mentions */
.mention {
  font-weight: 600; color: var(--violet);
  background: var(--violet-tint);
  padding: 0 4px; border-radius: 5px;
}
.msg-group.is-you .mention {
  color: #fff; background: rgba(255, 255, 255, .18);
}

/* ----- composer ----- */
.chat-composer {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 18px;
  border-top: 1px solid var(--rule-soft);
}
.chat-composer .composer-field {
  flex: 1; min-width: 0;
  display: flex; align-items: center;
  background: var(--paper-soft);
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0 20px; height: 46px;
  transition: border-color .15s, background .15s;
}
.chat-composer .composer-field:focus-within {
  border-color: var(--violet-soft); background: var(--paper-card);
}
.chat-composer input {
  /* min-width:0 so the flex field can shrink below the input's intrinsic
     (size-attribute) width — without it the "Envoyer" button was pushed past
     the card edge and clipped on narrow phones. */
  flex: 1; min-width: 0; border: 0; background: transparent; outline: none;
  font-family: var(--font-body); font-size: 14.5px; color: var(--ink);
}
.chat-composer input::placeholder { color: var(--ink-dim); }
.chat-composer .send {
  flex-shrink: 0;
  width: 46px; height: 46px; border-radius: 50%;
  border: 0; background: var(--violet); color: #fefdfa;
  font-size: 19px; line-height: 1;
  display: grid; place-items: center;
  transition: transform .15s, box-shadow .15s;
}
.chat-composer .send:hover {
  transform: scale(1.06);
  box-shadow: 0 8px 20px -8px rgba(var(--violet-rgb),.6);
}

@keyframes msg-rise { to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .msg-group { animation: none; opacity: 1; transform: none; }
}

@media (max-width: 920px) {
  .chat-wrap { padding: 24px 16px 24px; }
  .chat-card { border-radius: 16px; }
  .chat-stream { padding: 18px 16px 8px; }
  .msg-stack { max-width: 84%; }
}


/* ==========================================================================
   [LOGIN] — front door : full-bleed poster medley + login panel
   Standalone page (login.html does not extend base.html).
   ========================================================================== */

.login-page {
  display: flex;
  overflow: hidden;
  background: var(--ink);
  background-image: none;
}

/* ---------- left : full-bleed poster medley + overlaid welcome ---------- */
.login-hero {
  flex: 1.32;
  position: relative;
  overflow: hidden;
  background: var(--ink);
}
.login-medley {
  position: absolute;
  inset: -70px -90px;
  display: grid;
  grid-template-columns: repeat(auto-fill, 150px);
  grid-auto-rows: 222px;
  gap: 14px;
  justify-content: center;
  align-content: center;
  transform: rotate(-7deg);
}
/* each tile carries a type placeholder underneath ; the real poster rides on
   top as a background-image layer (.login-mp-poster). A missing poster file
   leaves that layer transparent so the placeholder shows through — a pure-CSS
   fallback, no <img>/onerror. .poster-placeholder is the shared visual
   language. */
.login-mp {
  position: relative;
  border-radius: 11px;
  overflow: hidden;
  background: #000;
  box-shadow: 0 18px 32px -22px rgba(0, 0, 0, .85);
}
.login-mp-poster {
  position: absolute; inset: 0;
  background-size: cover;
  background-position: center;
}
/* per-type placeholder tint — medley only ; the shared .poster-placeholder
   stays violet everywhere else in the app. */
.login-mp--movie .poster-placeholder {
  color: var(--warn);
  background:
    radial-gradient(circle at 30% 30%, rgba(176, 140, 0, .34), transparent 60%),
    var(--ink);
}
.login-mp--tvshow .poster-placeholder {
  color: var(--accent-teal);
  background:
    radial-gradient(circle at 30% 30%, rgba(47, 132, 136, .34), transparent 60%),
    var(--ink);
}
.login-mp--videogame .poster-placeholder {
  color: var(--accent-rose);
  background:
    radial-gradient(circle at 30% 30%, rgba(176, 72, 96, .34), transparent 60%),
    var(--ink);
}

/* veil — lighter overall so the medley reads, with a soft dark pool under the
   text zone (left) so the welcome copy stays crisp */
.login-veil {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(840px 760px at 19% 50%, rgba(17, 14, 21, .62), transparent 72%),
    linear-gradient(102deg, rgba(19, 16, 23, .72) 0%, rgba(19, 16, 23, .55) 48%, rgba(19, 16, 23, .22) 100%);
}

.login-hero-content {
  position: relative;
  z-index: 2;
  height: 100%;
  padding: 54px 60px;
  display: flex;
  flex-direction: column;
}
.login-brand {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 33px;
  letter-spacing: -.022em;
  color: #f7f4f1;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  text-shadow: 0 2px 26px rgba(0, 0, 0, .5);
}
.login-brand::before {
  content: "";
  width: 36px;
  height: 36px;
  flex: none;
  background: url(../images/logos/cineapp-logo.png) center / contain no-repeat;
}
.login-hero-mid { margin: auto 0; }
.login-hero-mid h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 4.5vw, 60px);
  line-height: 1;
  letter-spacing: -.035em;
  color: #f7f4f1;
  max-width: 560px;
  margin-bottom: 20px;
  text-shadow: 0 2px 34px rgba(0, 0, 0, .55);
}
.login-hero-mid h1 em { font-style: italic; font-weight: 500; color: var(--violet-soft); }
.login-hero-mid p {
  font-size: 16.5px;
  line-height: 1.6;
  color: rgba(243, 239, 245, .66);
  max-width: 432px;
}
/* Grid with equal columns: the three pills always share the same width (sized
   to the widest, "Jeux vidéo") and never wrap, at any viewport width. */
.login-stats { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 12px; max-width: 470px; }
.login-stats > div {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 13px 12px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .08);
  border: 1px solid rgba(243, 239, 245, .17);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
  box-shadow: 0 10px 26px -16px rgba(0, 0, 0, .8);
}
.login-stats .n {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 25px;
  line-height: 1;
  letter-spacing: -.025em;
  color: #f9f6f3;
  font-feature-settings: "tnum" 1;
}
.login-stats .l {
  font-family: var(--font-ui);
  font-weight: 900;
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--violet-soft);
  white-space: nowrap;
}

/* ---------- right : login panel ---------- */
.login-panel {
  flex: .9;
  max-width: 540px;
  background: var(--paper-card);
  border-left: 1px solid var(--rule);
  padding: 54px 66px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.login-eyebrow {
  font-family: var(--font-ui);
  font-weight: 900;
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 14px;
}
.login-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 34px;
  letter-spacing: -.03em;
  margin-bottom: 32px;
}
.login-field { margin-bottom: 16px; }
.login-field label {
  display: block;
  margin-bottom: 7px;
  font-size: 10.5px;
}
.login-field input {
  width: 100%;
  height: 50px;
  padding: 0 18px;
  background: var(--paper-soft);
  border: 1px solid transparent;
  border-radius: 12px;
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  outline: none;
  transition: border-color .15s, background .15s;
}
.login-field input::placeholder { color: var(--ink-dim); }
.login-field input:focus { border-color: var(--violet-soft); background: var(--paper-card); }
.login-submit {
  width: 100%;
  height: 52px;
  margin-top: 10px;
  background: var(--violet);
  color: #fefdfa;
  border: 0;
  border-radius: 12px;
  font-family: var(--font-ui);
  font-weight: 900;
  font-size: 12px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform .15s, box-shadow .15s;
}
.login-submit:hover { transform: translateY(-2px); box-shadow: 0 14px 28px -12px rgba(var(--violet-rgb),.6); }
.login-guest { margin-top: 20px; text-align: center; font-size: 14px; color: var(--ink-mid); }
.login-guest a {
  color: var(--violet);
  font-weight: 500;
  border-bottom: 1px solid var(--violet-soft);
  cursor: pointer;
}
/* "Propulsé par" — TMDB & IGDB sponsor logos, shown bare below the form,
   centered on the white panel. */
.login-powered {
  margin-top: 34px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 15px;
}
.login-powered-label {
  font-size: 11px;
}
.login-powered-logos {
  display: flex;
  align-items: center;
  gap: 40px;
}
.login-powered-logos a {
  display: inline-flex;
  transition: opacity .15s;
}
.login-powered-logos a:hover { opacity: .6; }
.login-powered-logos img {
  height: 44px;
  width: auto;
  display: block;
}

/* flash — the /login route flashes bad-credential errors (category 'danger') */
.login-flash {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 18px;
  padding: 12px 16px;
  border-radius: 12px;
  font-size: 14px;
  background: rgba(176, 72, 96, .1);
  border: 1px solid rgba(176, 72, 96, .3);
  color: var(--error);
}
.login-flash.success {
  background: rgba(94, 122, 72, .1);
  border-color: rgba(94, 122, 72, .3);
  color: var(--ok);
}

@media (max-width: 1000px) {
  .login-page { display: block; overflow: auto; }
  /* Stacked layout: let the hero shrink to its content (the old 500px floor
     left a big empty band before the panel) and centre everything for a tidy,
     symmetric mobile hero instead of the desktop left-aligned column. */
  .login-hero { min-height: auto; }
  .login-hero-content { padding: 40px 28px; align-items: center; text-align: center; }
  .login-panel {
    max-width: none;
    border-left: 0;
    border-top: 1px solid var(--rule);
    padding: 40px 28px;
  }

  /* Same equal-width grid, but the pills stack (number over label) and the
     whole row is capped and centred. Still no wrap, down to the smallest phones. */
  .login-stats { display: grid; width: min(340px, 100%); margin: 28px auto 0; gap: 8px; }
  .login-stats > div { flex-direction: column; align-items: center; gap: 3px; padding: 10px 8px; }
  .login-stats .n { font-size: 21px; }
  .login-stats .l { font-size: 8.5px; letter-spacing: .08em; white-space: nowrap; }
}

/* ==========================================================================
   [EDIT_PROFILE] — user profile editor (edit_profile.html)
   Two states (profil / mot de passe) share the .profile-grid shell.
   ========================================================================== */

.profile-wrap { max-width: 1060px; margin: 0 auto; padding: 0 32px 70px; }

/* ---------- tabs : profil / mot de passe ---------- */
.profile-tabs {
  display: inline-flex; gap: 4px; padding: 4px; margin-bottom: 28px;
  background: var(--paper-soft); border-radius: 999px;
}
.profile-tabs a {
  padding: 9px 22px; border-radius: 999px;
  font-family: var(--font-ui); font-weight: 900; font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim);
  transition: color .15s;
}
.profile-tabs a:hover { color: var(--ink); }
.profile-tabs a.active {
  background: var(--paper-card); color: var(--violet);
  box-shadow: 0 1px 4px rgba(28, 24, 32, .1);
}

/* ---------- two-column layout ---------- */
.profile-grid {
  display: grid; grid-template-columns: 296px 1fr;
  gap: 34px; align-items: start;
}
/* Les items de grille ont min-width:auto (= largeur du contenu) : sans ce reset,
   le min-content de la carte d'identité en mobile force la colonne — donc tout le
   formulaire — plus large que le viewport, et le contenu est rogné. */
.profile-grid > * { min-width: 0; }

/* ---------- identity card ---------- */
.id-card {
  padding: 34px 26px 28px; text-align: center;
}
.id-ava-wrap { position: relative; width: 128px; height: 128px; margin: 0 auto 18px; }
.id-ava-wrap::before {
  content: ""; position: absolute; inset: -9px; border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, var(--violet-soft), transparent 70%);
  opacity: .55;
}
.id-ava {
  width: 128px; height: 128px; font-size: 56px;
  background: linear-gradient(150deg, var(--violet), #4a2770);
  box-shadow: 0 14px 30px -16px rgba(74, 39, 112, .8);
}
.id-name {
  font-family: var(--font-display); font-style: italic;
  font-weight: 600; font-size: 25px; letter-spacing: -.02em; margin-bottom: 4px;
}
.id-role {
  font-size: 10.5px;
}
.id-sep { margin: 22px 0 18px; border-top: 1px dashed var(--rule); }
.photo-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 18px; border-radius: 10px;
  background: var(--paper-soft); border: 1px solid var(--rule);
  font-family: var(--font-ui); font-weight: 900; font-size: 11px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink); cursor: pointer;
  transition: border-color .15s, color .15s;
}
.photo-btn:hover { border-color: var(--violet-soft); color: var(--violet); }
.photo-hint { margin-top: 10px; font-size: 12px; color: var(--ink-dim); }
/* Upload confirmation — hidden until the file input reports a selected image
   (JS toggles .show; not the [hidden] attr, which the display below would override). */
.photo-loaded { display: none; margin-top: 8px; align-items: center; gap: 6px;
  font-family: var(--font-ui); font-weight: 900; font-size: 11px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--violet); }
.photo-loaded.show { display: inline-flex; }

/* ---------- form panels ---------- */
.profile-panel {
  padding: 26px 30px; margin-bottom: 22px;
}
.panel-label {
  font-size: 11px;
  margin-bottom: 4px;
}
.panel-title {
  font-family: var(--font-display); font-style: italic;
  font-weight: 600; font-size: 22px; letter-spacing: -.02em; margin-bottom: 20px;
}
.panel-title.tight { margin-bottom: 6px; }
.panel-intro { font-size: 14px; color: var(--ink-mid); margin-bottom: 20px; max-width: 46ch; }

/* ---------- text fields ---------- */
.profile-field { margin-bottom: 16px; }
.profile-field:last-child { margin-bottom: 4px; }
.profile-field label {
  display: block;
  margin-bottom: 7px;
  font-size: 10.5px;
}
.profile-field input {
  width: 100%; height: 50px; padding: 0 18px;
  background: var(--paper-soft); border: 1px solid transparent; border-radius: 12px;
  font: inherit; font-size: 15px; color: var(--ink); outline: none;
  transition: border-color .15s, background .15s;
}
.profile-field input::placeholder { color: var(--ink-dim); }
.profile-field input:focus { border-color: var(--violet-soft); background: var(--paper-card); }
.profile-err { display: block; margin-top: 7px; font-size: 13px; color: var(--error); }

/* ---------- notification toggles ---------- */
.notif-toggle, .photo-input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.notif {
  display: flex; align-items: center; gap: 18px;
  padding: 15px 0; border-bottom: 1px solid var(--rule-soft);
}
.notif:last-child { border-bottom: 0; padding-bottom: 2px; }
.notif .txt { flex: 1; }
.notif .n-name { font-size: 15px; font-weight: 500; color: var(--ink); }
.notif .n-desc { font-size: 13px; color: var(--ink-mid); margin-top: 2px; }
.profile-switch {
  flex-shrink: 0; width: 46px; height: 26px; border-radius: 999px;
  background: var(--rule); position: relative; cursor: pointer;
  transition: background .2s;
}
.profile-switch::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .28); transition: left .2s;
}
.notif-toggle:checked + .profile-switch { background: var(--violet); }
.notif-toggle:checked + .profile-switch::after { left: 23px; }
.notif-toggle:focus-visible + .profile-switch {
  outline: 2px solid var(--violet-soft); outline-offset: 2px;
}

/* ---------- save bar ---------- */
.save-bar { display: flex; justify-content: flex-end; align-items: center; gap: 18px; }
.save-hint { font-size: 13px; color: var(--ink-dim); }
.profile-save {
  height: 52px; padding: 0 38px; border: 0; border-radius: 12px;
  background: var(--violet); color: #fefdfa; cursor: pointer;
  font-family: var(--font-ui); font-weight: 900; font-size: 12px;
  letter-spacing: .16em; text-transform: uppercase;
  transition: transform .15s, box-shadow .15s;
}
.profile-save:hover { transform: translateY(-2px); box-shadow: 0 14px 28px -12px rgba(var(--violet-rgb),.6); }

@media (max-width: 840px) {
  .profile-wrap { padding: 0 20px 50px; }
  .profile-grid { grid-template-columns: 1fr; gap: 20px; }
  .id-card { display: flex; flex-wrap: wrap; align-items: center; gap: 20px; text-align: left; padding: 20px; }
  .id-ava-wrap { margin: 0; width: 80px; height: 80px; flex-shrink: 0; }
  .id-ava { width: 80px; height: 80px; font-size: 34px; }
  .id-sep { display: none; }
  .id-card .photo-btn { margin-left: auto; }
  .photo-hint { display: none; }
}
