/* /Components/Layout/AppShell.razor.rz.scp.css */
/*
  Two breakpoints, and no third (blazor-ui rule 3):
    base   - one column; the identity strip is a sticky compact header (FR-026)
    40rem  - roomier gutters only, same structure
    64rem  - rail and surface side by side, rail 300-340px (FR-025)
*/

/*
  L'ONGLET AVANT LE RAIL, SOUS 64REM (amendement A10 du 2026-09-12).

  Le propriétaire, sur le prototype : « en mode mobile faceit et régularité sont
  peut-être moins importants que les réseaux et le reste ». Sur un téléphone, le
  rail arrivait entre l'en-tête et l'onglet : il fallait passer la carte FaceIT
  et sept lignes d'horaires avant de voir les réseaux, la une et les clips.

  Le balisage ne change pas : les trois régions restent dans le même ordre dans
  le document, et ce sont les zones nommées qui changent de place. L'ordre du
  clavier et celui d'un lecteur d'écran restent donc ceux de l'affichage
  (research R6) - c'est exactement ce qu'un réordonnancement en CSS casse quand
  on s'y prend autrement.
*/
.shell[b-pufw1dsie6] {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas:
        "identity"
        "surface"
        "rail";
    /*
      The explicit row sizes matter as much as the areas. Left to "auto", the
      three rows stretch proportionally to fill min-height, which inflated the
      compact header to 215px of a 1112px viewport for 56px of content, and
      opened a matching void under the rail. The surface takes the slack
      instead; the header and the rail stay the size of what is in them.

      Depuis l'amendement A10 la ligne qui prend le mou est la deuxième, celle
      de l'onglet, puisque c'est elle qui est au milieu.
    */
    grid-template-rows: auto 1fr auto;
    min-height: 100dvh;
}

/*
  FR-026: this is the compact header. It is a direct child of the page-height
  grid, which is what lets it stay visible for the whole scroll, and it holds
  only the identity strip, which is what keeps it a thin band rather than half
  the phone screen.
*/
.shell__identity[b-pufw1dsie6] {
    grid-area: identity;
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    background-color: var(--color-surface);
    border-block-end: var(--border-width) solid var(--color-border-strong);
    padding: var(--space-3) var(--page-gutter);
}

/*
  Two blocks since feature 005 - the FaceIT card and the regularity calendar -
  where there used to be one, so the column needs a gap. Without it the two
  cards touch and read as one badly drawn block, which is what the owner saw.
*/
.shell__rail[b-pufw1dsie6] {
    grid-area: rail;
    display: flex;
    flex-direction: column;
    /* Les sections du rail s'ouvrent maintenant sous un filet (amendement A5),
       et le filet est la séparation : un écart de plus les décollerait de lui. */
    gap: 0;
    /* Sous 64rem le rail est passé après l'onglet (A10), donc il finit la page :
       il lui faut sa marge basse, que la surface portait avant lui. */
    padding: 0 var(--page-gutter) var(--space-7);
    min-width: 0;
}

/*
  Column plus gap rather than a margin rule on the children: the tab bar and the
  tab content are separate components, and CSS isolation scopes a selector like
  "> * + *" to this component's own markup, where they do not live. A gap is a
  property of the container, so it works across that boundary without ::deep.
*/
.shell__surface[b-pufw1dsie6] {
    grid-area: surface;
    /*
      Grid rather than flex, and the reason is FR-010 of feature 002: the tab
      bar takes what it needs, the tab content takes everything that is left.
      That is what fills the frame, and it fills it with the content that
      already exists rather than with something invented to occupy it, which
      FR-014 forbids.

      Rows are declared rather than left to grid-auto-rows for the same reason
      the shell declares its own: "auto" rows under a stretched container
      distribute the slack instead of giving it to the row that should have it.
    */
    display: grid;
    grid-template-rows: auto 1fr;
    gap: var(--space-5);
    padding: var(--space-5) var(--page-gutter) var(--space-7);
    /*
      A grid item's automatic minimum size is its content, so without this a
      wide child pushes the column past the viewport instead of shrinking -
      the usual cause of the horizontal scrollbar FR-027 of 001 forbids.
    */
    min-width: 0;
}

@media (min-width: 40rem) {
    .shell__identity[b-pufw1dsie6],
    .shell__rail[b-pufw1dsie6],
    .shell__surface[b-pufw1dsie6] {
        padding-inline: var(--page-gutter-wide);
    }
}

@media (min-width: 64rem) {
    .shell[b-pufw1dsie6] {
        grid-template-columns: minmax(var(--rail-min), var(--rail-max)) 1fr;
        grid-template-areas:
            "identity surface"
            "rail     surface";
        grid-template-rows: auto 1fr;
        column-gap: var(--space-6);
        max-width: var(--page-max);
        margin-inline: auto;
    }

    /*
      Side by side, so the identity strip stops being a header: static flow,
      stacked above the rest of the rail in the same column. Not sticky here on
      purpose - on a short desktop viewport (1440x720 is one of the checked
      sizes) a sticky column taller than the window would need its own
      scrollbar, and the spec asks for ordinary vertical scrolling instead.
    */
    .shell__identity[b-pufw1dsie6] {
        position: static;
        background-color: transparent;
        border-block-end: none;
        border-inline-end: var(--border-width) solid var(--color-border-strong);
        /*
          The top of the rail is tighter than the top of the surface: that is
          what fits identity, live block and card into 900px without scrolling
          (FR-018a of 003). Every pixel taken here is a pixel the visitor does
          not have to scroll past.

          Tightened again once CI showed the rail overflowing on Linux while it
          fitted on Windows. The target is no longer "fits at 900" but "fits
          with room to spare", because 900 is already generous: a browser's own
          chrome takes a hundred pixels off a 900px screen before the page gets
          any.
        */
        padding-block: var(--space-5) var(--space-5);
        /* Moved into the Skins tab's plate on navigation (012, amendment A2; app.css). */
        view-transition-name: shell-frame;
    }

    .shell__rail[b-pufw1dsie6] {
        border-inline-end: var(--border-width) solid var(--color-border-strong);
        padding-block: 0 var(--space-7);
    }

    .shell__surface[b-pufw1dsie6] {
        padding-block: var(--space-7);
        view-transition-name: shell-surface;
    }
}
/* /Components/Layout/IdentityPlate.razor.rz.scp.css */
/*
  Base: the compact header below 64rem - avatar, name, and the socials pushed to
  the end of the row. Above 64rem: the plate, a rounded column.
*/
.plate[b-0509fadoob] {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.plate__avatar[b-0509fadoob] {
    position: relative;
    flex: none;
    display: block;
    border-radius: var(--radius-full);
    text-decoration: none;
}

.plate__disc[b-0509fadoob] {
    display: grid;
    place-items: center;
    inline-size: var(--avatar-size-compact);
    block-size: var(--avatar-size-compact);
    overflow: hidden;
    border-radius: var(--radius-full);
    background-color: var(--color-plate);
    box-shadow: var(--shadow-sm);
    transition: box-shadow var(--duration-base) var(--ease-out);
}

.plate__photo[b-0509fadoob] {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
}

.plate__mark[b-0509fadoob] {
    inline-size: 100%;
    block-size: 100%;
    padding: var(--space-1);
    object-fit: contain;
}

/* Offline: the presence dot, ringed off the avatar by the plate's own ground. */
.plate__presence[b-0509fadoob] {
    position: absolute;
    inset-inline-end: 0;
    inset-block-end: 0;
    display: none;
    inline-size: var(--presence-size);
    block-size: var(--presence-size);
    border-radius: var(--radius-full);
    background-color: var(--color-border-strong);
    box-shadow: 0 0 0 var(--presence-ring) var(--color-surface);
}

.plate__avatar[data-state="offline"] .plate__presence[b-0509fadoob] {
    display: block;
}

/* Live: the ring, and the tag astride the foot of the avatar. */
.plate__avatar[data-state="live"] .plate__disc[b-0509fadoob] {
    box-shadow: 0 0 0 var(--presence-ring) var(--live-emphasis);
}

.plate__live[b-0509fadoob] {
    position: absolute;
    inset-inline-start: 50%;
    inset-block-end: calc(-1 * var(--space-2));
    display: none;
    padding-inline: var(--space-1);
    transform: translateX(-50%);
    border-radius: var(--radius-xs);
    background-color: var(--live-tag-surface);
    color: var(--color-text);
    font-family: var(--font-display);
    font-size: var(--text-tag);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    line-height: var(--leading-normal);
}

.plate__avatar[data-state="live"] .plate__live[b-0509fadoob] {
    display: block;
}

/* Announced only when the avatar is a link, as LiveBadge does. */
.plate__external[b-0509fadoob] {
    display: none;
}

.plate__avatar[data-state="live"] .plate__external[b-0509fadoob] {
    display: inline;
}

.plate__name[b-0509fadoob] {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    line-height: var(--leading-tight);
}

.plate__socials[b-0509fadoob] {
    display: flex;
    gap: var(--space-2);
    margin: 0 0 0 auto;
    padding: 0;
    list-style: none;
}

.plate__social[b-0509fadoob] {
    --brand: var(--brand-default);

    display: grid;
    place-items: center;
    inline-size: var(--tap-target-min);
    block-size: var(--tap-target-min);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-full);
    background-color: var(--color-plane-quiet);
    transition:
        background-color var(--duration-fast) var(--ease-out),
        border-color var(--duration-fast) var(--ease-out);
}

.plate__social:hover[b-0509fadoob] {
    background-color: var(--color-plane);
    border-color: var(--brand);
}

.plate__social[data-brand="twitch"][b-0509fadoob] {
    --brand: var(--brand-twitch);
}

.plate__social[data-brand="discord"][b-0509fadoob] {
    --brand: var(--brand-discord);
}

.plate__social[data-brand="x"][b-0509fadoob] {
    --brand: var(--brand-x);
}

.plate__social[data-brand="csgorep"][b-0509fadoob] {
    --brand: var(--brand-csgorep);
}

.plate__glyph[b-0509fadoob] {
    inline-size: var(--icon-size);
    block-size: var(--icon-size);
    background-color: var(--brand);
    -webkit-mask: var(--plate-icon) center / contain no-repeat;
    mask: var(--plate-icon) center / contain no-repeat;
}

@media (min-width: 64rem) {
    .plate[b-0509fadoob] {
        flex-direction: column;
        gap: var(--space-4);
        block-size: 100%;
        padding: var(--space-5) var(--space-2);
        border: var(--border-width) solid var(--color-edge);
        border-radius: var(--radius-tile);
        background-color: var(--color-plane-quiet);
        box-shadow: var(--shadow-card);
        transition: border-color var(--duration-base) var(--ease-out);
    }

    /* Live, the top of the plate warms up: seen from the corner of the eye. */
    .plate:has(.plate__avatar[data-state="live"])[b-0509fadoob] {
        border-color: color-mix(in srgb, var(--live-emphasis) var(--plate-live-edge-mix), transparent);
        background-image: linear-gradient(to bottom, var(--live-block-from), transparent var(--plate-live-glow-stop));
    }

    .plate__disc[b-0509fadoob] {
        inline-size: var(--plate-avatar-size);
        block-size: var(--plate-avatar-size);
        view-transition-name: shell-avatar;
    }

    .plate__name[b-0509fadoob] {
        font-size: var(--text-base);
    }

    /* Sous le nom, pas en bas de la colonne (012, A7, FR-059). Collés en bas,
       les réseaux descendaient avec l'inventaire : sur une liste fournie il y
       avait un écran de vide entre le nom et eux, et on finissait par ne plus
       les voir. La plaque, elle, garde sa hauteur. */
    .plate__socials[b-0509fadoob] {
        flex-direction: column;
        margin: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .plate__disc[b-0509fadoob],
    .plate[b-0509fadoob],
    .plate__social[b-0509fadoob] {
        transition: none;
    }
}
/* /Components/Layout/IdentityRail.razor.rz.scp.css */
/*
  This component is the compact header of FR-026 below 1024px and the top of
  the rail above it.

  Base is the header: avatar and text on one row, live badge on the next, sized
  to stay a thin band over the page. At 64rem it becomes the top of the rail and
  everything grows.
*/

/*
  The badge gets its own line at every width, compact header included. Sharing
  a row with the name meant that widening the badge - "Hors ligne" to "Statut
  indisponible" is some 45px - pushed the name and the tagline sideways, which
  is exactly the movement FR-010 forbids.
*/
.rail[b-bmicrxzy85] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
}

.rail__identity[b-bmicrxzy85] {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

/*
  The avatar disc. Fixed size in each layout, so swapping the mark for a photo
  cannot change anything (SC-010). The light plate is what keeps the mark's dark
  strokes readable (FR-005).
*/
.rail__avatar[b-bmicrxzy85] {
    flex: none;
    display: grid;
    place-items: center;
    inline-size: var(--avatar-size-compact);
    block-size: var(--avatar-size-compact);
    overflow: hidden;
    border-radius: var(--radius-full);
    background-color: var(--color-plate);
    box-shadow: var(--shadow-sm);
}

.rail__photo[b-bmicrxzy85] {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
}

.rail__mark[b-bmicrxzy85] {
    inline-size: 100%;
    block-size: 100%;
    padding: var(--space-1);
    object-fit: contain;
}

.rail__text[b-bmicrxzy85] {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.rail__name[b-bmicrxzy85] {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    line-height: var(--leading-tight);
}

/*
  FR-006: the tagline reserves the height of two lines whatever it currently
  says, so replacing it with a longer text (up to fifteen words) moves nothing
  below it. Derived from tokens with calc(), never a hard-coded height.
*/
.rail__tagline[b-bmicrxzy85] {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    min-block-size: calc(2 * var(--text-sm) * var(--leading-normal));
}

/*
  The block keeps its reserved box - height here, the rest in LiveBadge's own
  sheet - so that moving from one state to another pushes nothing (FR-018 of
  002). Since 002 the badge is a block: it takes the rail's width and its
  height comes from its own token.
*/
.rail__badge[b-bmicrxzy85] {
    display: grid;
    flex: none;
    /* Stretched, because a flex column child otherwise shrinks to its content
       and the block's width follows the length of its label - exactly the
       movement FR-018 forbids. The height comes from the block itself. */
    align-self: stretch;
}

/*
  Above 64rem the rail has to fit a 900px viewport without scrolling
  (FR-018a of 003), and these two gaps are where the margin comes from.

  They were 24px and 16px, tuned on Windows, where the whole rail ended at
  893px of 900 - seven pixels of margin, which is not margin. The same page on
  the CI runner ended at 911px and scrolled: Chromium lays this text out about
  eighteen pixels taller on Linux, and Linux is what the container ships. A
  layout that only fits on the machine it was written on does not fit.
*/
@media (min-width: 64rem) {
    .rail[b-bmicrxzy85] {
        gap: var(--space-4);
    }

    .rail__identity[b-bmicrxzy85] {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-3);
    }

    .rail__avatar[b-bmicrxzy85] {
        inline-size: var(--avatar-size);
        block-size: var(--avatar-size);
        /* Shrinks into the Skins tab's plate on navigation (012, amendment A2; app.css). */
        view-transition-name: shell-avatar;
    }

    .rail__name[b-bmicrxzy85] {
        font-size: var(--text-display);
    }

    .rail__tagline[b-bmicrxzy85] {
        font-size: var(--text-base);
        min-block-size: calc(2 * var(--text-base) * var(--leading-normal));
    }
}
/* /Components/Layout/PlateShell.razor.rz.scp.css */
/*
  The two breakpoints of AppShell, and the same base (012, amendment A2):
    base   - one column; the plate is the sticky compact header
    40rem  - roomier gutters only
    64rem  - the plate as a narrow column, sticky, the surface beside it
*/
.plate-shell[b-lxssb75amb] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    min-height: 100dvh;
}

.plate-shell__plate[b-lxssb75amb] {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    background-color: var(--color-surface);
    border-block-end: var(--border-width) solid var(--color-border-strong);
    padding: var(--space-3) var(--page-gutter);
}

/* The surface as AppShell lays it: the tab bar, then everything left. */
.plate-shell__surface[b-lxssb75amb] {
    display: grid;
    grid-template-rows: auto 1fr;
    gap: var(--space-5);
    padding: var(--space-5) var(--page-gutter) var(--space-7);
    min-width: 0;
}

@media (min-width: 40rem) {
    .plate-shell__plate[b-lxssb75amb],
    .plate-shell__surface[b-lxssb75amb] {
        padding-inline: var(--page-gutter-wide);
    }
}

@media (min-width: 64rem) {
    /*
      Against the left edge of the window, not centred with the page (amendment
      A3): the owner, on the preprod, « la barre de gauche doit être poussée
      beaucoup plus à gauche ». The plate is a frame, not content; the inventory
      takes every pixel it leaves.
    */
    .plate-shell[b-lxssb75amb] {
        grid-template-columns: var(--plate-width) minmax(0, 1fr);
        grid-template-rows: 1fr;
        column-gap: var(--space-6);
        padding-inline-start: var(--page-gutter);
    }

    /*
      A column the height of the window, held in view for the whole scroll: the
      socials at its foot stay reachable beside the fiftieth fiche. Its height is
      the window's less its two margins, so it never needs a scrollbar of its
      own - the plate holds an avatar, a name and four icons.
    */
    .plate-shell__plate[b-lxssb75amb] {
        top: var(--space-4);
        align-self: start;
        block-size: calc(100dvh - 2 * var(--space-4));
        margin-block: var(--space-4);
        padding: 0;
        background-color: transparent;
        border-block-end: none;
        view-transition-name: shell-frame;
    }

    .plate-shell__surface[b-lxssb75amb] {
        padding-block: var(--space-7);
        padding-inline: 0 var(--page-gutter-wide);
        view-transition-name: shell-surface;
    }
}
/* /Components/Layout/TabBar.razor.rz.scp.css */
.tabs__list[b-1bh5ohp1n1] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    list-style: none;
    margin: 0;
    padding: 0;
    border-block-end: var(--border-width) solid var(--color-border-strong);
}

.tabs__tab[b-1bh5ohp1n1] {
    font-family: var(--font-display);
    letter-spacing: var(--tracking-wide);
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    /* WCAG 2.5.5, and it keeps the three tabs the same height. */
    min-block-size: var(--tap-target-min);
    padding-inline: var(--space-4);
    border-block-end: var(--focus-ring-width) solid transparent;
    color: var(--color-text-muted);
    font-weight: var(--weight-medium);
    text-decoration: none;
    cursor: pointer;
    transition: color var(--duration-fast) var(--ease-out);
}

.tabs__tab:hover[b-1bh5ohp1n1] {
    color: var(--color-text);
}

/*
  The active tab is marked by aria-current, so the styling keys off the same
  attribute rather than a class. One source of truth: a tab cannot look active
  without being announced as the current page.
*/
.tabs__tab[aria-current="page"][b-1bh5ohp1n1] {
    color: var(--color-text);
    border-block-end-color: var(--color-accent);
}

/*
  Disabled tabs stay legible - they are an announcement, not a mistake - but
  read as unavailable, and the cursor says so too.
*/
.tabs__tab[disabled][b-1bh5ohp1n1] {
    color: var(--color-text-muted);
    cursor: not-allowed;
}

.tabs__tab[disabled]:hover[b-1bh5ohp1n1] {
    color: var(--color-text-muted);
}

.tabs__soon[b-1bh5ohp1n1] {
    padding-inline: var(--space-2);
    border: var(--border-width) solid var(--color-border-strong);
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
}
/* /Components/Pages/Home.razor.rz.scp.css */
/*
  The Réseaux tab's content, stacked.

  The link cards take the height they are due - fixed tile rows from 40rem, per
  feature 002 - and the broadcast module takes what is left. That order is what
  fills the frame with the module rather than with a gap: rows of "auto 1fr"
  give the slack to the second block, and the second block is the one this
  feature added to hold it.

  It lives here rather than in AppShell because FR-020 freezes the shell, and
  because the arrangement of one tab's blocks is that tab's business. Another tab
  will stack different things.
*/
/*
  Three rows, all "auto", and none of them stretching.

  This has now been wrong twice in opposite ways. "auto 1fr" for three children
  pinned the last module to the bottom of the frame. Then "auto auto 1fr" put the
  stretch on the third row - and when the owner asked for the clips to come
  first, the markup swapped and this did not, so the slack landed on the
  broadcasts. Their box stretched invisibly to the bottom, which is worse than
  the first version: nothing looked wrong, and the SC-001 guard measured that
  stretched box and dutifully reported no void at all.

  A stretching row was never the right answer. Feature 002 already settled the
  principle - FR-014 forbids inventing content to occupy space - and a row that
  stretches is a box pretending there is content in it. The blocks take the
  height they need, the leftover collects at the bottom where it can be seen and
  measured, and in the nominal state there is none because the content overruns
  the frame.
*/
.surface-stack[b-znf8qwlcwr] {
    display: grid;
    /*
      The column is declared, and minmax(0, 1fr) rather than left implicit.

      An implicit column is sized to the min-content of its widest child, and
      this stack holds the four link cards. On Linux their min-content is 326px:
      the column took 326 inside a 288px surface and every block below inherited
      it, which the CI reported as 22px of overflow at 320px - twice, on a page
      that is green on Windows.

      The cards are feature 001's and they are not at fault. Wrapping them in a
      grid is what exposed it, and min-width:0 on the stack does not help: it
      bounds the stack, not the track inside it.
    */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    align-content: start;
    gap: var(--space-6);
    min-width: 0;
}
/* /Components/Pages/Skins.razor.rz.scp.css */
/*
  L'onglet Skins, empilé (012).

  La même règle que l'onglet Réseaux : des rangées qui prennent la hauteur de ce
  qu'elles tiennent, et aucune qui s'étire - une rangée étirée est une boîte qui
  fait semblant d'avoir du contenu.
*/
.skins-stack[b-yan7yenar5] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    gap: var(--space-6);
    min-width: 0;
}

/*
  Les fiches, autant de colonnes que la largeur en tient, sans breakpoint : deux
  sur téléphone depuis le resserrement de l'A5, six ou sept sur un écran large.
  auto-fill plutôt qu'auto-fit, la leçon des cartes de rediffusion de la 011 :
  une page de deux fiches ne les étire pas sur toute la largeur.
*/
.skins-grid[b-yan7yenar5] {
    display: grid;
    /* Pas de borne haute : voir --fiche-min. La colonne unique d'un téléphone
       étroit prend toute la rangée plutôt que d'en laisser 40 % vide. */
    grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--fiche-min)), 1fr));
    /* Toutes les fiches à la même hauteur (amendement A3) : chaque rangée prend
       celle de la plus haute de toute la liste, et la fiche remplit sa case -
       son pied, poussé en bas, s'aligne d'une fiche à l'autre. */
    grid-auto-rows: 1fr;
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.skins-grid__item[b-yan7yenar5] {
    display: grid;
    min-inline-size: 0;
}

/*
  Le pied de page (A7, FR-063). Trois faits posés sur une rangée qui se replie :
  quand l'inventaire a été relevé, où le voir sur Steam, d'où viennent les prix.
  Un trait les sépare de la liste, rien d'autre - ce n'est pas une carte, c'est
  la fin de la page.
*/
.skins-foot[b-yan7yenar5] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-5);
    margin: var(--space-5) 0 0;
    padding-block-start: var(--space-4);
    border-block-start: var(--border-width) solid var(--color-border);
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

.skins-foot__link[b-yan7yenar5] {
    color: var(--color-accent);
}

/* Une recherche sans résultat : une phrase, le terme rappelé, et le chemin du
   retour. */
.skins-none[b-yan7yenar5] {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.skins-none__clear[b-yan7yenar5] {
    color: var(--color-accent);
}

/* Le second moyen de contact : une phrase posée, pas une carte - elle ne doit
   pas disputer l'attention au bouton de chaque fiche. */
.skins-contact[b-yan7yenar5] {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.skins-contact__link[b-yan7yenar5] {
    color: var(--color-accent);
}
/* /Components/Shared/CountryFlag.razor.rz.scp.css */
/*
  The two colours a flag needs, handed to the drawing through custom properties
  rather than painted on each shape.

  Custom properties inherit, so the root carries them and every shape inside
  resolves its own - which means CSS isolation only has to reach the root
  element. Painting each rect and polygon from a descendant selector would rely
  on the scope attribute landing on SVG children, and a colour that silently
  falls back to black is exactly the kind of defect a green test suite does not
  catch.
*/
.flag[data-code="cn"][b-782gygva8h] {
    --flag-field: var(--flag-cn-field);
    --flag-star: var(--flag-cn-star);
}

.flag[data-code="eu"][b-782gygva8h] {
    --flag-field: var(--flag-eu-field);
    --flag-star: var(--flag-eu-star);
}

.flag[b-782gygva8h] {
    flex: none;
    border-radius: var(--radius-xs);
}

.flag--drawn[b-782gygva8h] {
    inline-size: var(--flag-width);
    /* The 3:2 both flags are specified in. Without it the drawing takes its
       height from the line box and stretches. */
    aspect-ratio: 3 / 2;
    block-size: auto;
    /* A hairline, because a light field on a dark surface bleeds into it and the
       flag loses its edge. Inset so it does not grow the box. */
    outline: var(--border-width) solid var(--color-border);
    outline-offset: calc(var(--border-width) * -1);
}

/*
  The fallback wears the shape of a flag rather than the shape of an error: same
  corner, same weight beside the rank, so a rail showing one drawn flag and one
  code does not look as though something failed.

  It is deliberately not held to the flag's width. Two letters at the smallest
  type in the scale do not fit inside 1.25rem, and squeezing them would trade a
  readable fallback for a tidy one.
*/
.flag--code[b-782gygva8h] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-inline: var(--space-1);
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wide);
    color: var(--color-text-muted);
    background-color: var(--color-surface-raised);
}
/* /Components/Shared/DetailBubble.razor.rz.scp.css */
/*
  The bubble: a small card that floats, and is there at once - the StreamsCharts
  shape the owner pointed at. Every value is a token (blazor-ui rule 1).

  Hidden by default, and revealed on the parent's :hover only where the device
  has a fine pointer that can hover (FR-011). A finger has neither, and a tap
  would leave a bubble stuck open: it gets the card's sentences, as a phone
  does (FR-002b). A tablet with a mouse gets the bubbles. That is a question about
  the pointer, not about the width, so it adds no breakpoint.

  NO TRANSITION, on purpose. The native bubble's second of delay is what made it
  feel unfinished, and a fade would bring a smaller version of the same thing
  back. A state change in CSS lands on the next frame.

  The parent's :hover works from here because isolation scopes the rightmost
  selector only: ":hover > .bubble" is ":hover > .bubble[b-…]", true under any
  hovered parent that holds a bubble - no ::deep needed.
*/
.bubble[b-dg7y2eg8hl] {
    display: none;
    position: absolute;
    z-index: var(--z-overlay);
    inset-block-end: var(--bubble-offset, calc(100% + var(--bubble-gap)));
    inset-inline-start: 0;
    inline-size: var(--bubble-width);
    overflow: hidden;
    border: var(--border-width) solid var(--color-rule);
    border-radius: var(--radius-bubble);
    background-color: var(--bubble-surface);
    box-shadow: var(--shadow-lg);
    font-size: var(--text-xs);
    font-weight: var(--weight-regular);
    line-height: var(--leading-tight);
    color: var(--color-text-muted);
    text-align: start;
    white-space: normal;
    overflow-wrap: anywhere;
    /* A bubble never catches the pointer: moving onto it is moving off the
       element, and the neighbour's bubble must take over at once (FR-003). */
    pointer-events: none;
}

/* The second half of a row opens towards the start, so it stays in the column
   (research R2). */
.bubble[data-edge="end"][b-dg7y2eg8hl] {
    inset-inline-start: auto;
    inset-inline-end: 0;
}

/* An element at the very top of what clips it has nothing above to open into:
   the wear on a skin plinth opens under itself instead (012, amendment A6).
   --bubble-offset keeps the same meaning, read from the other edge. */
.bubble[data-side="below"][b-dg7y2eg8hl] {
    inset-block-end: auto;
    inset-block-start: var(--bubble-offset, calc(100% + var(--bubble-gap)));
}

/*
  Two sections, each with its own padding, split by a rule: what is
  described, and what it was (FR-002a).
*/
.bubble__header[b-dg7y2eg8hl] {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
}

.bubble__titles[b-dg7y2eg8hl] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-inline-size: 0;
}

.bubble__heading[b-dg7y2eg8hl] {
    font-family: var(--font-display);
    font-size: var(--text-base);
    font-weight: var(--weight-bold);
    color: var(--color-text);
}

.bubble__caption[b-dg7y2eg8hl] {
    font-size: var(--text-xs);
}

.bubble__body[b-dg7y2eg8hl] {
    display: block;
    padding: var(--space-3) var(--space-4);
    border-block-start: var(--border-width) solid var(--color-rule);
}

/*
  An icon is a mask painted with a colour: the shape lives once in tokens.css,
  and fifty bubbles do not carry fifty copies of an SVG.
*/
.bubble__icon[b-dg7y2eg8hl] {
    flex: none;
    display: block;
    inline-size: var(--bubble-icon-size);
    block-size: var(--bubble-icon-size);
    background-color: var(--color-accent);
    -webkit-mask: var(--bubble-icon) center / contain no-repeat;
    mask: var(--bubble-icon) center / contain no-repeat;
}

.bubble__icon[data-icon="calendar"][b-dg7y2eg8hl] {
    --bubble-icon: var(--icon-calendar);
}

@media (hover: hover) and (pointer: fine) {
    :hover > .bubble[b-dg7y2eg8hl] {
        display: block;
    }
}
/* /Components/Shared/FaceitCard.razor.rz.scp.css */
/*
  The FaceIT card.

  Its height follows its content, with two reserved heights and no more - see
  the note above the media query for which states get one and why.

  The tier hue arrives through a data attribute and drives two things: the
  ring's crown and the ambient wash (FR-036). A tier-3 card is green, a tier-10
  card is red. The colour informs, it does not decorate.
*/

/*
  SANS BOÎTE DEPUIS LE 2026-09-12 (amendements A3 et A5).

  Le propriétaire, sur la colonne de gauche : « j'ai l'impression qu'on est un
  peu too much en mode carte ? le bandeau gauche en mode pc pourrait être autre
  chose que des cartes ? » Deux blocs sur trois du rail ne se cliquaient pas et
  portaient pourtant la même boîte que celui-ci. La règle qui en sort : une
  carte veut dire un clic. Le rail ouvre donc ses sections sous un filet, et
  cette carte-ci - qui mène au profil - est un lien sur toute sa surface plutôt
  qu'une boîte avec un bouton dedans.

  Le lavis de palier reste (FR-036 de la 002) : c'est lui qui porte la couleur
  du niveau, et il est la seule chose que le propriétaire a voulu garder de la
  boîte.
*/

.faceit[b-kq90nago2j] {
    /* Fallback for the unavailable state, which has no tier to show. */
    --palier: var(--color-accent);
    --palier-text: var(--color-accent);

    display: flex;
    flex-direction: column;
    /* Tightened from --space-3 for FR-018a: the rail has to fit a 900px
       viewport on Linux as well as on Windows, and five gaps at four pixels
       less is twenty pixels the visitor does not scroll. It also reads denser,
       which is the direction this card was already going. */
    gap: var(--space-2);
    /*
      Ouverte sur les côtés, séparée du bloc au-dessus par un filet : la section
      commence où le filet la coupe, pas où une boîte la referme.

      De l'air des deux côtés du filet (retouche du 2026-09-12, vue sur la
      préprod) : « les lignes entre les "cartes" un peu collé ». Un filet qui
      touche le texte au-dessus de lui ne sépare pas deux sections, il souligne
      la première.
    */
    padding: var(--space-5) 0 var(--space-4);
    border-block-start: var(--border-width) solid var(--color-rule);
    color: var(--color-text);
    text-decoration: none;
    /*
      LE LAVIS D'AMBIANCE EST PARTI LE 2026-09-12 (retouche vue sur la préprod).

      Le propriétaire : « on a des délimitations pas forcément jolies, dégradé
      de couleur qui pop un peu ». Deux tentatives n'y ont rien fait, et la
      géométrie dit pourquoi : le dégradé partait du coin haut-droit, donc son
      centre - là où il est le plus dense - était posé sur le bord de la
      section. La moitié de l'ellipse tombait hors de la boîte, et ce bord-là
      est un trait droit quoi qu'on fasse. Sur une carte il coïncidait avec le
      cadre et ne se voyait pas ; sur une section ouverte, il n'y a plus de
      cadre pour l'expliquer.

      Ce que la FR-036 de la 002 demande, c'est que la couleur du palier
      informe - pas qu'elle remplisse un rectangle. Elle reste donc dans
      l'anneau, dans l'elo au survol, dans le filet ci-dessous, et dans la
      bande des V/D.
    */
    position: relative;
}

/*
  Le filet de tête prend la teinte du palier, de gauche à droite : neutre au
  début, palier à la fin. Un trait de 1 px est net par nature, donc il ne peut
  pas « popper » comme une surface - et il dit à quelle section il appartient.
*/
.faceit[b-kq90nago2j]::before {
    content: "";
    position: absolute;
    inset-block-start: calc(-1 * var(--border-width));
    inset-inline: 0;
    block-size: var(--border-width);
    background-image: linear-gradient(
        to right,
        var(--color-rule),
        color-mix(in srgb, var(--palier) 60%, transparent)
    );
    pointer-events: none;
}

/* Au survol, l'elo prend la couleur du palier : le seul signe qu'on est sur un
   lien, et il vient de la donnée plutôt que d'un décor. */
.faceit:hover .faceit__elo-value[b-kq90nago2j] {
    color: var(--palier-text);
}

/* L'anneau de focus entoure la carte entière, puisque la carte entière est le
   lien (A3). Il suit la géométrie ouverte : pas de rayon de carte ici. */
.faceit:focus-visible[b-kq90nago2j] {
    outline: var(--focus-ring-width) solid var(--color-focus);
    outline-offset: var(--focus-ring-offset);
    border-radius: var(--radius-sm);
}

.faceit[data-palier="palier-1"][b-kq90nago2j] {
    --palier: var(--palier-1);
    --palier-text: var(--palier-1-text);
}

.faceit[data-palier="palier-2"][b-kq90nago2j] {
    --palier: var(--palier-2);
    --palier-text: var(--palier-2-text);
}

.faceit[data-palier="palier-4"][b-kq90nago2j] {
    --palier: var(--palier-4);
    --palier-text: var(--palier-4-text);
}

.faceit[data-palier="palier-8"][b-kq90nago2j] {
    --palier: var(--palier-8);
    --palier-text: var(--palier-8-text);
}

.faceit[data-palier="palier-10"][b-kq90nago2j] {
    --palier: var(--palier-10);
    --palier-text: var(--palier-10-text);
}

/*
  A reserved height applies above 64rem only, where the card lives in a 300px
  rail. Below that it takes the full width, is naturally shorter, and holding it
  to a reserved height dug a 300px hole under a single sentence.

  ONE state has it now, and the arithmetic of feature 005 is why. The loaded
  card was measured in every shape it can take:

    both rankings, all measures  31.91rem
    no rankings, all measures    28.20rem
    both rankings, no measures   23.35rem
    neither                      19.63rem
    identity alone               11.69rem

  Twenty rem of spread inside one state. Reserving the tallest would dig up to
  twenty rem of void under the bottom-pinned button whenever a ranking or a
  measure is missing - which is precisely the fault feature 003 removed when the
  four season states stopped sharing a height, and the fault of 001 before it.
  So the loaded card takes its natural height, and the token that reserved one
  is gone.

  Unavailable keeps its own, for the opposite reason: its content is 5.14rem,
  and a card that size reads as debris rather than as a state. Any state added
  here gets a reserved height only once its content has been measured against
  it.

  Nothing jumps as a result. This page is statically server-rendered: a card
  that is shorter on one load than the next is two loads, not a shift.

  THE LAST RESERVED HEIGHT WENT ON 2026-09-12 (amendement A5). It existed
  because a box holding one sentence read as debris; with no box left, there is
  no box to look wrong - a kicker and a sentence on the rail's ground read as a
  state, and eleven rem of reserved nothing would read as the hole the whole
  batch removes elsewhere.
*/

.faceit__kicker[b-kq90nago2j] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--brand-faceit-text);
}

.faceit__identity[b-kq90nago2j] {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

/*
  The progress ring.

  A conic gradient and an inner disc: no image, no script, no library. The
  travelled share arrives through --faceit-progress, set inline by the
  component. That is data, not a design value: FR-029 forbids hard-coded
  colours and sizes, not the display of a computed number.
*/
.faceit__badge[b-kq90nago2j] {
    position: relative;
    display: grid;
    place-items: center;
    flex: none;
    inline-size: var(--ring-size);
    block-size: var(--ring-size);
    border-radius: var(--radius-full);
    background-image: conic-gradient(
        var(--palier) 0 var(--faceit-progress, 0%),
        var(--palier-track) var(--faceit-progress, 0%) 100%
    );
}

.faceit__badge[b-kq90nago2j]::before {
    content: "";
    position: absolute;
    inset: var(--ring-thickness);
    border-radius: var(--radius-full);
    /* Le fond du rail depuis que la section est ouverte (A5) : sur une carte
       c'était --color-surface, et le disque se verrait comme une pastille
       grise sur le fond de page. */
    background-color: var(--color-bg);
}

.faceit__level[b-kq90nago2j] {
    position: relative;
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    color: var(--palier-text);
    font-variant-numeric: tabular-nums;
}

.faceit__who[b-kq90nago2j] {
    /* Lets a long nickname wrap rather than widening the rail. */
    min-inline-size: 0;
}

/*
  The nickname, alone under the elo since the tier and the division rank left
  it in 005. The rankings now sit in their own list, with flags.
*/
.faceit__rank[b-kq90nago2j] {
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    color: var(--color-text);
    /* Lets a long nickname wrap rather than widening the rail. */
    overflow-wrap: anywhere;
}

/*
  The two rankings, one per line. A row each rather than a pair side by side:
  in a 300px rail two flags and two ordinals on one line leave the ordinals
  fighting for space, and the whole point of the flags is that they are read at
  a glance.
*/
/*
  The two rankings side by side rather than stacked, and a measurement decided
  it: on the Linux runner the card ended at 893.9px of a 900px fold, eighteen
  pixels past the margin the layout is held to. Chromium lays this text out
  taller there than on Windows, which is why only the CI can settle it.

  One row rather than two gives back exactly that, and it loses nothing: two
  flags with two positions take 140px of a 268px card, and reading them side by
  side is if anything closer to what the profile does. Wrapping is allowed, so a
  four-digit rank in both scopes falls back to the old shape instead of
  overflowing.
*/
.faceit__rankings[b-kq90nago2j] {
    display: flex;
    flex-wrap: wrap;
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.faceit__ranking[b-kq90nago2j] {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.faceit__position[b-kq90nago2j] {
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
    color: var(--color-text);
}

/*
  What the window covers, said in words above the figures. It is small and
  muted on purpose: it qualifies the figures rather than competing with them.
*/
.faceit__window[b-kq90nago2j] {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/*
  La bande de score (amendement A4 du 2026-09-12).

  Cinq colonnes sur une ligne, filets fins entre elles, têtes courtes en
  capitales : le tableau des scores de CS2, que le public de ce site lit sans y
  penser. C'était cinq tuiles empilées en deux colonnes, dont une seule sur sa
  ligne, et c'est ce qui donnait au rail son air de tableau de bord.

  Cinq colonnes tiennent dans un rail de 300px parce que chaque valeur est
  courte : « 18 / 30 » est la plus large, et elle est en chiffres tabulaires.
  Sous 64rem la bande prend toute la largeur de l'onglet, donc elle respire.
*/
.faceit__figures[b-kq90nago2j] {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    margin: 0;
    padding-block: var(--space-2);
    border-radius: var(--radius-sm);
    background-color: var(--color-plane-quiet);
}

.faceit__figure[b-kq90nago2j] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    padding-inline: var(--space-1);
    text-align: center;
    /* A long value must never widen the band: it is a column of a grid, and
       the grid's tracks are equal by design. */
    min-inline-size: 0;
}

/* Le filet qui sépare deux colonnes, comme dans un tableau de score. */
.faceit__figure + .faceit__figure[b-kq90nago2j] {
    border-inline-start: var(--border-width) solid var(--color-rule);
}

.faceit__label[b-kq90nago2j] {
    font-family: var(--font-display);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.faceit__value[b-kq90nago2j] {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.faceit__elo[b-kq90nago2j] {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
}

.faceit__elo-value[b-kq90nago2j] {
    font-family: var(--font-display);
    font-size: var(--text-display);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    font-variant-numeric: tabular-nums;
}

.faceit__elo-unit[b-kq90nago2j] {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.faceit__form[b-kq90nago2j] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.faceit__results[b-kq90nago2j] {
    display: flex;
    gap: var(--space-1);
    list-style: none;
    margin: 0;
    padding: 0;
}

/*
  The letter always doubles the colour (SC-008): the form stays readable in
  greyscale and to a colour-blind reader.
*/
.faceit__result[b-kq90nago2j] {
    display: grid;
    place-items: center;
    inline-size: var(--space-5);
    block-size: var(--space-5);
    border-radius: var(--radius-sm);
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
}

.faceit__result[data-result="win"][b-kq90nago2j] {
    background-color: var(--result-win-surface);
    color: var(--result-win);
}

.faceit__result[data-result="loss"][b-kq90nago2j] {
    background-color: var(--result-loss-surface);
    color: var(--result-loss);
}

/*
  The run in progress (FR-009b), in the space the strip's row already reserved.

  It costs no height, and that is the whole reason it is here: the card ended
  24px above a 900px fold with nothing to spare, so a line of its own pushed it
  through. Right-aligned and allowed to wrap inside its own box - two short
  lines beside a 24px strip still cost the card nothing.

  The count carries the colour, the words carry the meaning - so the line says
  as much in greyscale as in colour (SC-008), and a losing run is written in the
  same red as the D markers beside it rather than escalated into an alarm.
*/
.faceit__streak[b-kq90nago2j] {
    margin: 0;
    text-align: end;
    text-wrap: balance;
    font-size: var(--text-2xs);
    line-height: var(--leading-tight);
    color: var(--color-text-muted);
}

.faceit__streak-count[b-kq90nago2j] {
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
}

.faceit__streak[data-result="win"] .faceit__streak-count[b-kq90nago2j] {
    color: var(--result-win);
}

.faceit__streak[data-result="loss"] .faceit__streak-count[b-kq90nago2j] {
    color: var(--result-loss);
}

.faceit__note[b-kq90nago2j] {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

/*
  The unavailable state is centred in the room it occupies: read that way, it
  presents itself as an intended state rather than as content that failed to
  arrive.
*/
.faceit[data-state="unavailable"] .faceit__note[b-kq90nago2j] {
    margin-block: auto;
    text-align: center;
}

/*
  Le bouton « Voir le profil FaceIT » est parti le 2026-09-12 (amendement A3).
  Toute la carte est le lien : « le bouton est un peu useless ? on peut cliquer
  sur la carte ? ». Ses styles partent avec lui, et --tap-target-min n'a plus à
  être tenu par un bouton ici - c'est la carte entière qui est la cible.
*/

/* /Components/Shared/LiveBadge.razor.rz.scp.css */
/*
  The live block - the page's only shout.

  The box is identical in all three states (FR-018): same minimum height, same
  padding, same width. Only the colour and the intensity change, which is
  exactly what site.js can do without touching the layout.

  ROUNDED SINCE 2026-09-12 (amendement A1, FR-023 et SC-006 de la 002).

  It was the site's one angular object, and 002 wrote the exit in advance: « si
  le voisinage ne tient pas, la sortie de secours est écrite : le bloc de direct
  rejoint la géométrie arrondie et la dominance repose alors entièrement sur la
  couleur, la taille et le contraste ». The owner, seeing the block beside the
  new cards: « en format PC c'est pas cohérent, le format arène ne va plus là ».
  So the cut is gone, the geometry is the page's, and the block still dominates
  by its red, its size and its contrast - which is what SC-004 measures.

  The plate stays a separate element even without a cut: it is what paints the
  background behind the rows, and it is what keeps the focus ring on the anchor
  rather than on a painted box.
*/

.badge[b-nbtetbpy5z] {
    position: relative;
    display: flex;
    flex-direction: column;
    /*
      Le contenu part du haut (retouche du 2026-09-12, vue sur la préprod).

      Le propriétaire : « la carte hors ligne un peu grosse pour pas grand
      chose, en mode pc ». La boîte garde une hauteur réservée - sinon le
      passage en direct, que site.js fait sans recharger la page, décalerait
      tout ce qui est en dessous (FR-010 de la 001) -, mais elle est ramenée à
      ce dont l'état en direct a besoin, et le contenu est ancré en haut. Ce qui
      reste d'air se lit alors comme l'écart entre deux sections, et non comme
      une ligne qui flotte au milieu d'un bloc vide.
    */
    justify-content: flex-start;
    gap: var(--space-2);
    min-block-size: var(--live-block-min-height);
    /*
      Rien sur les côtés, dans les trois états (amendement A5).

      Hors ligne le bloc est une ligne posée sur le fond du rail, alignée sur
      les sections en dessous ; en direct il s'allume en carte. Le texte ne
      bouge pas d'un état à l'autre - c'est la plaque qui s'élargit, par ses
      décalages négatifs - et FR-018 de la 002 l'exige : le bloc est mis à jour
      par site.js, et un texte qui se décale au changement d'état est
      exactement le mouvement qu'elle interdit.
    */
    padding-block: var(--space-3);
    color: var(--color-text-muted);
    text-decoration: none;
    /* No background, no border here: everything is painted by the plate. */
    background: none;
    border: none;
}

/*
  La plaque : elle ne peint que l'état en direct (amendement A5).

  Une carte veut dire un clic, et le bloc n'est un lien qu'en direct - hors
  ligne il n'a pas d'adresse où aller, donc pas de boîte. Elle s'étend au-delà
  du texte pour que la carte rouge respire sans que le texte, lui, se déplace.
*/
.badge__plate[b-nbtetbpy5z] {
    position: absolute;
    inset-block: 0;
    inset-inline: calc(-1 * var(--space-3));
    z-index: 0;
    border-radius: var(--radius-card);
}

.badge[data-state="live"] .badge__plate[b-nbtetbpy5z] {
    border: var(--border-width) solid var(--color-edge);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.badge__row[b-nbtetbpy5z] {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.badge__dot[b-nbtetbpy5z] {
    inline-size: var(--space-3);
    block-size: var(--space-3);
    flex: none;
    border-radius: var(--radius-full);
    background-color: currentcolor;
}

.badge__label[b-nbtetbpy5z] {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wide);
    line-height: var(--leading-tight);
}

/*
  L'heure habituelle, à côté du statut (amendement A2 du 2026-09-12).

  Sur téléphone seulement, et hors ligne seulement : au-dessus de 64rem, la
  carte Régularité est dans le rail, visible sans défiler, et le propriétaire a
  écarté la pastille là - « en mode pc c'est sûr que ça fait trop ». En dessous,
  la Régularité est passée après l'onglet (amendement A10), donc l'heure est
  loin : c'est exactement où cette pastille sert.

  Elle ne réserve pas de place quand elle n'est pas là : c'est une ligne du
  `badge__row`, dont la hauteur est déjà celle du label.
*/
.badge__pill[b-nbtetbpy5z] {
    display: none;
    padding: var(--space-1) var(--space-3);
    margin-inline-start: var(--space-1);
    border-radius: var(--radius-chip);
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    white-space: nowrap;
}

.badge[data-state="offline"] .badge__pill[b-nbtetbpy5z] {
    display: inline-block;
}

/*
  Visible in the live state only, because that is the only one where the block
  is a link. Driven by the state attribute, so it follows when site.js changes
  it, without one more line of JavaScript.
*/
.badge__hint[b-nbtetbpy5z] {
    position: relative;
    z-index: 1;
    display: none;
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--live-hint);
}

/*
  The "opens in a new tab" mention. It carries no appearance of its own -
  .visually-hidden in app.css does that - so all this pair adds is the state:
  the block is a link when it is live and inert otherwise, and an inert anchor
  opens no tab to announce. Unlike the invitation above, it is not behind a
  breakpoint: an accessible name that changes with the viewport is a bug.
*/
.badge__external[b-nbtetbpy5z] {
    display: none;
}

.badge[data-state="live"] .badge__external[b-nbtetbpy5z] {
    display: inline;
}

/* ------------------------------------------------------------------ live */

.badge[data-state="live"][b-nbtetbpy5z] {
    color: var(--live-on-block);
}

.badge[data-state="live"] .badge__plate[b-nbtetbpy5z] {
    background-image: radial-gradient(
        120% 130% at 100% 0%,
        var(--live-block-from) 0%,
        var(--live-block-to) 70%
    );
}

.badge[data-state="live"] .badge__dot[b-nbtetbpy5z] {
    background-color: var(--live-emphasis);
    animation: badge-pulse-b-nbtetbpy5z 2s var(--ease-out) infinite;
}

@keyframes badge-pulse-b-nbtetbpy5z {
    0% {
        box-shadow: 0 0 0 0 var(--live-pulse-from);
    }

    70% {
        box-shadow: 0 0 0 0.75rem var(--live-pulse-to);
    }

    100% {
        box-shadow: 0 0 0 0 var(--live-pulse-to);
    }
}

/* ------------------------------------------------- the two quiet states */

/*
  FR-019: no halo, no animation, no alert colour. An unknown status is not a
  warning, and a channel that is off air is not a failure.
*/

.badge[data-state="offline"] .badge__dot[b-nbtetbpy5z] {
    background-color: var(--color-border-strong);
}

.badge[data-state="unknown"][b-nbtetbpy5z] {
    color: var(--color-text-muted);
}

/*
  L'état inconnu n'a plus de plaque en retrait depuis l'amendement A5 : il n'y a
  plus de plan à mettre en retrait, puisque le bloc est posé sur le fond du
  rail. Ce qui le distingue reste ce qui le distinguait déjà - ses mots, et son
  point éteint.
*/

.badge[data-state="unknown"] .badge__dot[b-nbtetbpy5z] {
    background-color: var(--color-border);
}

/*
  FR-020: under reduced motion the pulse disappears and the information stays
  whole - it was carried by the text, the colour and the size, never by the
  movement.
*/
@media (prefers-reduced-motion: reduce) {
    .badge[data-state="live"] .badge__dot[b-nbtetbpy5z] {
        animation: none;
    }
}

/*
  Above 64rem the block is no longer inside a sticky header: it sits at the top
  of the rail.

  Il n'y prend plus une hauteur plus grande depuis le 2026-09-12 : les deux
  réserves - la bande compacte et le rail - sont devenues une seule, parce que
  l'état le plus rempli tient maintenant sur une ligne aux deux largeurs. Ce qui
  change ici, c'est la disposition, pas la boîte.
*/
@media (min-width: 64rem) {
    .badge[b-nbtetbpy5z] {
        /*
          L'invitation passe à côté du statut, et non plus dessous (retouche du
          2026-09-12, vue sur la préprod).

          Le propriétaire : « la carte hors ligne un peu grosse pour pas grand
          chose, en mode pc ». Cette hauteur-là n'est pas celle de « Hors
          ligne » : c'est celle de l'état en direct, le plus rempli, que la
          boîte réserve pour que le passage en direct ne décale rien (FR-010 de
          la 001). Deux lignes réservées pour en afficher une, c'est trente
          pixels d'air hors ligne - alors que les deux tiennent sur une ligne
          dans un rail de 300px.

          Une grille de deux colonnes, dans les trois états : hors ligne la
          deuxième colonne n'a rien dedans et se réduit à zéro, donc rien ne
          bouge quand site.js change l'état.
        */
        display: grid;
        grid-template-columns: 1fr auto;
        align-items: center;
        column-gap: var(--space-3);
    }

    .badge__hint[b-nbtetbpy5z] {
        grid-column: 2;
    }

    /* Sur PC, la pastille disparaît : la Régularité est dans le rail, à
       portée d'œil, avec l'horaire de chaque jour (amendement A2). */
    .badge[data-state="offline"] .badge__pill[b-nbtetbpy5z] {
        display: none;
    }

    .badge__label[b-nbtetbpy5z] {
        font-size: var(--text-xl);
    }

    /*
      The invitation appears here only, and only when live. In the compact
      header every line is paid for: FR-026 of 001 limits that band to a fifth
      of the screen, and "live" is already carried by the label, the colour and
      the pulsing dot.
    */
    .badge[data-state="live"] .badge__hint[b-nbtetbpy5z] {
        display: block;
    }
}
/* /Components/Skins/FloatRuler.razor.rz.scp.css */
/*
  La règle d'usure (012, research R10).

  Cinq bandes à arêtes franches, pas un dégradé : un float de 0,149 est encore
  « Très peu usée » et un de 0,151 est déjà « Testée sur le terrain », et un
  dégradé ferait croire à une usure qui glisse de l'une à l'autre. Les seuils
  sont ceux du jeu, en jetons.
*/

.ruler[b-47h2xbk39z] {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-inline-size: 0;
}

.ruler__track[b-47h2xbk39z] {
    position: relative;
    flex: 1;
    block-size: var(--float-track-height);
    border-radius: var(--radius-full);
    background: linear-gradient(
        to right,
        var(--wear-fn) 0 var(--wear-stop-fn),
        var(--wear-mw) var(--wear-stop-fn) var(--wear-stop-mw),
        var(--wear-ft) var(--wear-stop-mw) var(--wear-stop-ft),
        var(--wear-ww) var(--wear-stop-ft) var(--wear-stop-ww),
        var(--wear-bs) var(--wear-stop-ww) 100%);
}

.ruler__dot[b-47h2xbk39z] {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: var(--float-position);
    inline-size: var(--float-dot-size);
    block-size: var(--float-dot-size);
    border-radius: var(--radius-full);
    background-color: var(--color-text);
    box-shadow: 0 0 0 var(--float-dot-ring) var(--color-surface);
    transform: translate(-50%, -50%);
}

.ruler__value[b-47h2xbk39z] {
    font-size: var(--text-2xs);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
/* /Components/Skins/SkinCartBar.razor.rz.scp.css */
/*
  La barre du panier et son panneau (013, FR-021, direction U1).

  COLLÉE EN BAS, TOUJOURS, panier vide compris : sur cent objets, une barre en
  fin de page oblige à redescendre pour savoir ce qu'on a pris, et une barre qui
  n'arrive qu'au premier ajout fait bouger la page au moment précis où l'on vient
  de cliquer. Le propriétaire a choisi R1 sur maquette.

  La place qu'elle occupe est réservée par la page (Skins.razor.css), en CSS :
  rien ici ne se mesure en JavaScript.
*/
.cart[b-pfct3v3g6s] {
    position: sticky;
    inset-block-end: var(--cart-bar-gap);
    z-index: var(--z-sticky);
    margin-block-start: var(--space-5);
}

.cart[hidden][b-pfct3v3g6s] {
    display: none;
}

/*
  LE PANNEAU S'OUVRE AU-DESSUS (U1) : il flotte, il n'écarte rien. Sans cela,
  ouvrir le panier pousserait la liste entière et la carte qu'on regardait
  partirait de l'écran - c'est précisément ce que « on garde le fil » voulait
  dire.
*/
.cart__panel[b-pfct3v3g6s] {
    position: absolute;
    inset-inline: 0;
    inset-block-end: calc(100% + var(--space-2));
    max-block-size: var(--cart-panel-max);
    overflow-y: auto;
    padding: var(--space-3);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-raised);
    box-shadow: var(--shadow-lg);
}

.cart__panel[hidden][b-pfct3v3g6s] {
    display: none;
}

/*
  LE MESSAGE SOUS LES YEUX (amendement A3 du 2026-10-07, direction B2).

  Il est posé en haut du panneau, séparé de la liste par un filet : ce sont deux
  choses différentes - ce qui partira, puis le détail de ce qu'on a pris. Le
  cadre sombre le pose comme une citation, pas comme une zone de saisie : rien
  ne s'y modifie.
*/
.cart__draft[b-pfct3v3g6s] {
    margin-block-end: var(--space-3);
    padding-block-end: var(--space-3);
    border-block-end: var(--border-width) solid var(--color-rule);
}

.cart__draftline[b-pfct3v3g6s] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin: 0 0 var(--space-1);
}

.cart__drafttitle[b-pfct3v3g6s] {
    font-size: var(--text-2xs);
    color: var(--color-text-muted);
}

.cart__copy[b-pfct3v3g6s] {
    min-block-size: var(--tap-target-min);
    padding-inline: var(--space-3);
    border: var(--border-width) solid var(--color-accent);
    border-radius: var(--radius-chip);
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
    font-family: var(--font-display);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    cursor: pointer;
}

.cart__copy:hover[b-pfct3v3g6s] {
    background-color: var(--color-accent-quiet-strong);
}

/*
  white-space: pre-wrap, parce que le message porte ses propres retours à la
  ligne et que c'est exactement ce qui sera collé. Un aperçu qui reflue le texte
  autrement que ce qui part serait un aperçu qui ment.

  Plafonné et défilant : à six objets le message fait une quinzaine de lignes, et
  il ne doit pas chasser la liste et la barre hors de l'écran d'un téléphone.
*/
.cart__text[b-pfct3v3g6s] {
    margin: 0;
    max-block-size: var(--cart-text-max);
    overflow-y: auto;
    padding: var(--space-2);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-sm);
    background-color: var(--color-bg);
    font-size: var(--text-2xs);
    line-height: var(--leading-normal);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.cart__list[b-pfct3v3g6s] {
    display: grid;
    margin: 0;
    padding: 0;
    list-style: none;
}

.cart__row[b-pfct3v3g6s] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-raised);
    box-shadow: var(--shadow-lg);
}

.cart__count[b-pfct3v3g6s] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--tap-target-min);
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-text);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    cursor: pointer;
}

/*
  Panier vide : il n'y a rien à déplier, et ça se voit (amendement A2 du
  2026-10-07). Le script pose `disabled`, qui retire déjà le bouton du clavier
  et le dit aux lecteurs d'écran ; il reste à ne plus promettre un clic à l'œil.
  Le chevron part avec : une flèche vers un panneau qui n'existe pas est un
  mensonge, pas une décoration.
*/
.cart__count:disabled[b-pfct3v3g6s] {
    color: var(--color-text-muted);
    cursor: default;
}

.cart__count:disabled .cart__caret[b-pfct3v3g6s] {
    visibility: hidden;
}

.cart__caret[b-pfct3v3g6s] {
    color: var(--color-text-muted);
    font-size: var(--text-xs);
}

/* Ouvert, le chevron pointe vers le bas : il dit où mène le prochain clic. */
.cart__count[aria-expanded="true"] .cart__caret[b-pfct3v3g6s] {
    transform: rotate(180deg);
}

.cart__total[b-pfct3v3g6s] {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.cart__total b[b-pfct3v3g6s] {
    color: var(--color-text);
    font-family: var(--font-display);
    font-weight: var(--weight-bold);
}

.cart__clear[b-pfct3v3g6s] {
    min-block-size: var(--tap-target-min);
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-text-muted);
    font: inherit;
    font-size: var(--text-xs);
    text-decoration: underline;
    cursor: pointer;
}

.cart__clear[hidden][b-pfct3v3g6s] {
    display: none;
}

.cart__go[b-pfct3v3g6s] {
    display: inline-flex;
    align-items: center;
    margin-inline-start: auto;
    min-block-size: var(--tap-target-min);
    padding-inline: var(--space-4);
    border-radius: var(--radius-chip);
    background-color: var(--color-accent);
    color: var(--color-text-on-accent);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    text-decoration: none;
}

/* Panier vide : il n'y a personne à qui écrire (FR-012). Un lien désactivé
   n'existe pas en HTML - on lui retire donc ce qui en fait un lien. */
.cart__go[aria-disabled="true"][b-pfct3v3g6s] {
    background-color: var(--color-plane);
    color: var(--color-text-muted);
    pointer-events: none;
}

/* ---------------------------------------------------------------------------
   Une ligne du panier
   --------------------------------------------------------------------------- */
.cart__item[b-pfct3v3g6s] {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-block: var(--space-2);
    border-block-end: var(--border-width) solid var(--color-rule);
}

.cart__item:last-child[b-pfct3v3g6s] {
    border-block-end: 0;
}

.cart__thumb[b-pfct3v3g6s] {
    flex: none;
    inline-size: var(--cart-thumb-width);
    block-size: auto;
    border-radius: var(--radius-sm);
    background-color: var(--skin-plinth);
    object-fit: contain;
}

.cart__name[b-pfct3v3g6s] {
    display: grid;
    gap: var(--space-1);
    min-inline-size: 0;
    font-family: var(--font-display);
    font-size: var(--text-sm);
}

.cart__note[b-pfct3v3g6s] {
    font-family: var(--font-sans);
    font-size: var(--text-2xs);
    font-weight: var(--weight-regular);
    color: var(--color-text-muted);
}

.cart__price[b-pfct3v3g6s] {
    margin-inline-start: auto;
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.cart__drop[b-pfct3v3g6s] {
    flex: none;
    display: grid;
    place-items: center;
    inline-size: var(--tap-target-min);
    block-size: var(--tap-target-min);
    border: 0;
    border-radius: var(--radius-full);
    background: none;
    color: var(--color-text-muted);
    font-size: var(--text-lg);
    line-height: 1;
    cursor: pointer;
}

.cart__drop:hover[b-pfct3v3g6s] {
    color: var(--color-text);
}

/*
  UN OBJET QUI N'EST PLUS PROPOSÉ (FR-008). « Il faut que le user comprenne »
  (le propriétaire, 2026-10-05) : il reste à sa place plutôt que de disparaître
  en silence, et son prix barré dit qu'il ne compte plus dans le total.
*/
.cart__item[data-gone][b-pfct3v3g6s] {
    opacity: var(--cart-gone-opacity);
}

.cart__item[data-gone] .cart__thumb[b-pfct3v3g6s] {
    filter: grayscale(1);
}

.cart__item[data-gone] .cart__price[b-pfct3v3g6s] {
    font-weight: var(--weight-regular);
    color: var(--color-text-muted);
    text-decoration: line-through;
}

.cart__item[data-gone] .cart__note[b-pfct3v3g6s] {
    color: var(--quality-stattrak-text);
}

@media (prefers-reduced-motion: no-preference) {
    .cart__caret[b-pfct3v3g6s] {
        transition: transform var(--duration-fast) var(--ease-out);
    }
}

/*
  Ce que le panier a à dire : ce qu'il peut encore prendre, puis le refus quand
  il est plein. Vide, elle ne prend aucune place - une rangée réservée à un
  message qui n'arrive presque jamais serait une rangée perdue en permanence.
*/
.cart__notice[b-pfct3v3g6s] {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--quality-stattrak-text);
}

.cart__notice:empty[b-pfct3v3g6s] {
    display: none;
}
/* /Components/Skins/SkinFiche.razor.rz.scp.css */
/*
  La fiche « casier » (012, direction 1 du 2026-10-05).

  La forme du jeu : l'objet posé sur un socle sombre, sa rareté qui monte de
  dessous lui en lueur, un filet franc de la même couleur sous l'image, et le
  texte serré en dessous. Dix rem et demi de large, sept par ligne sur un écran
  large.

  Pourquoi pas la famille des cartes de la 011, qui habille Réseaux : ces
  tuiles-là sont faites pour quatre grandes cartes, et leur lavis à 18 % dans un
  coin disparaît sur une fiche de cette taille. Ici la couleur ne décore pas,
  elle nomme la rareté - donc elle doit se voir.
*/

.fiche[b-ymj4wtc7a5] {
    display: flex;
    flex-direction: column;
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    color: var(--color-text);
    /* Le socle et le filet touchent le bord : sans cela le coin arrondi laisse
       passer un angle de la couleur par-dessus la carte. */
    overflow: hidden;
    /* La carte mesure la largeur de ses bulles. --bubble-width vaut 16 rem par
       défaut (009) et la carte en fait douze : la bulle du prix sortait d'elle
       et la carte la coupait - « la bulle info sur le prix est croppée dans la
       carte » (le propriétaire, A6). Déclaré sur les ENFANTS : une unité cqi
       lue sur le conteneur lui-même interroge le conteneur du dessus. */
    container-type: inline-size;
}

/* Toute bulle de la carte, où qu'elle soit posée : elle prend la largeur de la
   carte moins la marge où vit l'élément qu'elle décrit. Sur TOUS les enfants et
   non sur deux d'entre eux - une bulle ajoutée demain dans les places de marché
   retomberait sinon sur les 16 rem et serait coupée comme celle du prix. */
.fiche > *[b-ymj4wtc7a5] {
    --bubble-width: calc(100cqi - 2 * var(--space-2));
}

/* La rareté, une fois pour toute la fiche : la lueur, le filet et la pastille
   la lisent sans savoir laquelle c'est. */
.fiche[data-rarity="default"][b-ymj4wtc7a5] { --rarity: var(--rarity-default); --rarity-text: var(--rarity-default-text); }
.fiche[data-rarity="common"][b-ymj4wtc7a5] { --rarity: var(--rarity-common); --rarity-text: var(--rarity-common-text); }
.fiche[data-rarity="uncommon"][b-ymj4wtc7a5] { --rarity: var(--rarity-uncommon); --rarity-text: var(--rarity-uncommon-text); }
.fiche[data-rarity="rare"][b-ymj4wtc7a5] { --rarity: var(--rarity-rare); --rarity-text: var(--rarity-rare-text); }
.fiche[data-rarity="mythical"][b-ymj4wtc7a5] { --rarity: var(--rarity-mythical); --rarity-text: var(--rarity-mythical-text); }
.fiche[data-rarity="legendary"][b-ymj4wtc7a5] { --rarity: var(--rarity-legendary); --rarity-text: var(--rarity-legendary-text); }
.fiche[data-rarity="ancient"][b-ymj4wtc7a5] { --rarity: var(--rarity-ancient); --rarity-text: var(--rarity-ancient-text); }
.fiche[data-rarity="immortal"][b-ymj4wtc7a5] { --rarity: var(--rarity-immortal); --rarity-text: var(--rarity-immortal-text); }

/* Une fiche sans offre possible : posée, pas encadrée. */
.fiche[data-link="absent"][b-ymj4wtc7a5] {
    background-color: var(--color-plane-quiet);
}

/* ---------------------------------------------------------------------------
   Le socle
   --------------------------------------------------------------------------- */
.fiche__plinth[b-ymj4wtc7a5] {
    position: relative;
    /* Il ne rétrécit pas. La carte est une colonne flex dans une rangée à
       hauteur fixe, et sans cela le socle se faisait écraser jusqu'à rogner
       l'image de quatre-vingts pixels (A4 du 2026-10-07, voir .fiche__shot).
       C'est le corps qui absorbe ce qui reste, et c'est son rôle : son prix est
       poussé en bas pour s'aligner d'une carte à l'autre. */
    flex: none;
    padding: var(--space-2) var(--space-2) var(--space-1);
    /* La lueur part de SOUS l'objet et monte : c'est ce qui le fait flotter
       plutôt que de le poser sur un fond teinté. */
    background-image: radial-gradient(
        90% 70% at 50% 108%,
        color-mix(in srgb, var(--rarity) var(--skin-glow-mix), transparent),
        transparent 70%
    );
    background-color: var(--skin-plinth);
    border-block-end: var(--skin-rarity-rule) solid var(--rarity);
}

/*
  L'IMAGE ÉTAIT ROGNÉE, ET DE BEAUCOUP (amendement A4 du 2026-10-07). « L'image
  du skin est cropée, la carte est haute pour rien. » Mesuré sur l'inventaire
  réel, à 1440 px : boîte de l'image 178 × 192, socle 194 × 119 — elle dépassait
  de 81 px, rognés par le socle.

  DEUX CAUSES QUI SE TENAIENT.

  1. `block-size: auto`. L'image porte `width="256" height="192"`, et cet
     attribut de hauteur fixe la boîte à 192 px. `aspect-ratio` ne s'applique
     que si une des deux dimensions est `auto` : il était donc inopérant, et la
     boîte faisait 178 × 192 au lieu de 178 × 134.

  2. `flex: none` sur le socle. La carte est une colonne flex dans une rangée à
     hauteur fixe (`grid-auto-rows: 1fr`, A3 de la 012, pour que les prix
     s'alignent). Le socle, lui, pouvait rétrécir : il tombait à 119 px et
     rognait l'image au lieu de la laisser respirer.

  Les deux ensemble : image entière, et la carte passe de 391 px à 288 px de
  haut. Le vide que le propriétaire voyait sous le nom était cette hauteur-là,
  réclamée par une image qu'on ne montrait pas.
*/
.fiche__shot[b-ymj4wtc7a5] {
    display: block;
    inline-size: 100%;
    max-inline-size: 100%;
    block-size: auto;
    aspect-ratio: var(--fiche-shot-aspect);
    object-fit: contain;
}

/*
  LA RANGÉE SOUS L'IMAGE (A10) : l'usure à gauche, le motif à droite. « L'état de
  l'arme en bas à gauche, motif en bas à droite mais juste le numéro. »

  Elle garde sa hauteur même vide, et c'est délibéré : une caisse n'a ni usure ni
  motif, et sans réserve son filet de rareté remonterait par rapport à celui de
  ses voisines. Une rangée de fiches doit aligner ses filets - c'est ce trait-là
  qu'on suit des yeux quand on balaie cent cartes.
*/
.fiche__marks[b-ymj4wtc7a5] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
    min-block-size: var(--fiche-marks-height);
    padding-block-start: var(--space-1);
    font-size: var(--text-2xs);
}

/* L'usure, à la couleur de la rareté : c'est la même famille d'information que
   la lueur et le filet, et elle n'a plus besoin de bulle depuis qu'elle est
   écrite en toutes lettres (A10). */
.fiche__wear[b-ymj4wtc7a5] {
    min-inline-size: 0;
    overflow-wrap: anywhere;
    color: var(--rarity-text);
}

/* Le motif : juste le nombre, et sa bulle dit ce que c'est. Il ne rétrécit pas -
   un numéro coupé ne désigne plus rien. */
.fiche__pattern[b-ymj4wtc7a5] {
    position: relative;
    flex: none;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
    cursor: help;
}

/*
  StatTrak et Souvenir sont sur la ligne de l'arme, dans le corps (A10) : ils
  qualifient l'arme et non l'image. En ligne, après le nom, séparés par l'espace
  de la ligne elle-même.
*/
.fiche__quality[b-ymj4wtc7a5] {
    /* Devant l'arme, donc l'espace est APRÈS (A10). Le jeu nomme l'objet
       « StatTrak™ M4A4 » : la qualité change ce qu'est l'arme, elle se lit donc
       avant elle. */
    margin-inline-end: var(--space-2);
    font-family: var(--font-display);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wide);
    /* La ligne de l'arme est en capitales ; ces deux-là sont des noms propres du
       jeu, qui les écrit « StatTrak™ » et « Souvenir ». Une marque criée en
       capitales n'est plus la marque. */
    text-transform: none;
}

.fiche__quality--stattrak[b-ymj4wtc7a5] { color: var(--quality-stattrak-text); }
.fiche__quality--souvenir[b-ymj4wtc7a5] { color: var(--quality-souvenir); }
/* ---------------------------------------------------------------------------
   Le corps
   --------------------------------------------------------------------------- */
.fiche__body[b-ymj4wtc7a5] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-2);
    /* Le pied se colle en bas : les prix s'alignent d'une fiche à l'autre, et
       c'est ce qu'on compare. */
    block-size: 100%;
}

.fiche__kind[b-ymj4wtc7a5],
.fiche__name[b-ymj4wtc7a5],
.fiche__trade[b-ymj4wtc7a5],
.fiche__deal[b-ymj4wtc7a5],
.fiche__markets[b-ymj4wtc7a5] {
    margin: 0;
}

.fiche__kind[b-ymj4wtc7a5] {
    font-family: var(--font-display);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

/* Blanc, et pas à la couleur de la rareté : la lueur et le filet la portent
   déjà, et deux fois la même couleur ne la rend pas plus lisible. */
.fiche__name[b-ymj4wtc7a5] {
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    overflow-wrap: anywhere;
}

.fiche__trade[b-ymj4wtc7a5] {
    font-size: var(--text-2xs);
    color: var(--color-text-muted);
}

/* Le prix seul depuis l'A7 : le bouton est descendu en pied de carte. La ligne
   est poussée en bas du corps, donc les prix s'alignent d'une fiche à l'autre -
   et c'est ce qu'on compare. */
.fiche__deal[b-ymj4wtc7a5] {
    display: flex;
    align-items: center;
    margin-block-start: auto;
    padding-block-start: var(--space-1);
}

.fiche__price-host[b-ymj4wtc7a5] {
    position: relative;
}

.fiche__price[b-ymj4wtc7a5] {
    font-family: var(--font-display);
    font-size: var(--text-base);
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
}

/* Ce qu'on ne sait pas n'est pas un montant, et ne s'écrit pas comme un. */
.fiche__price[data-price="unread"][b-ymj4wtc7a5],
.fiche__price[data-price="unlisted"][b-ymj4wtc7a5] {
    font-family: var(--font-sans);
    font-size: var(--text-2xs);
    font-weight: var(--weight-regular);
    color: var(--color-text-muted);
}

/* ---------------------------------------------------------------------------
   Les places de marché, puis l'action
   --------------------------------------------------------------------------- */

/*
  En pastilles à l'accent (A7, FR-057) : « les boutons CSFloat et Buff, un peu
  trop discrets ». Deux mots gris de onze pixels se lisaient comme une note de
  bas de page, alors qu'ils servent à vérifier le prix - ce qui est précisément
  ce qui rend le montant au-dessus crédible.
*/
.fiche__markets[b-ymj4wtc7a5] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-2);
    padding: 0 var(--space-2) var(--space-2);
    font-size: var(--text-2xs);
}

.fiche__market[b-ymj4wtc7a5] {
    padding: var(--market-chip-pad-block) var(--space-2);
    border-radius: var(--radius-chip);
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
    text-decoration: none;
}

.fiche__market:hover[b-ymj4wtc7a5] {
    background-color: var(--color-accent-quiet-strong);
}

/*
  L'ACTION EST UNE BANDE EN PIED (A7, FR-054), large comme la carte, collée à
  son bord, sans coins à elle - seuls ceux de la carte l'arrondissent. Elle était
  une pastille à côté du prix, et le propriétaire : « ça ne se fond pas du tout
  dans la carte ». C'est structurel, pas une question de couleur : la carte ne
  contient aucun autre objet à coins ronds et à fond plein.

  Le filet qui la tient est celui de la rareté de l'objet, le même qui tient le
  socle en haut : la carte est serrée entre deux traits de sa couleur.

  margin-block-start: auto n'y est pas - c'est .fiche__deal qui pousse déjà le
  prix en bas du corps, et la bande suit les places de marché.
*/
.fiche__demand[b-ymj4wtc7a5] {
    display: flex;
    align-items: center;
    justify-content: center;
    min-block-size: var(--tap-target-min);
    padding: var(--space-2);
    border-block-start: var(--skin-rarity-rule) solid var(--rarity);
    background-color: color-mix(in srgb, var(--rarity) var(--skin-demand-mix), transparent);
    color: var(--color-text);
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wide);
    text-decoration: none;
}

.fiche__demand:hover[b-ymj4wtc7a5] {
    background-color: color-mix(in srgb, var(--rarity) var(--skin-demand-mix-hover), transparent);
}

/*
  AU PANIER, et ça se voit AU REPOS (013, FR-004) : sans cela le visiteur perd
  le compte de ce qu'il a pris dès qu'il quitte une carte des yeux. Posé par le
  script sur la carte, jamais par le serveur - lui ne sait pas ce qui est pris.
*/
.fiche[data-in-cart="true"][b-ymj4wtc7a5] {
    border-color: var(--color-accent);
}

.fiche[data-in-cart="true"] .fiche__demand[b-ymj4wtc7a5] {
    border-block-start-color: var(--color-accent);
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
}

/*
  L.ANNEAU VERS L.INTÉRIEUR, pour celui-ci seulement. La bande touche les trois
  bords de la carte, et la carte masque ce qui dépasse d'elle (le socle y touche
  aussi) : un anneau posé à 2 px au-dehors n'aurait que son segment du haut de
  dessiné. Il n'est pas retiré - app.css l'interdit, et FR-028 avec - il est
  repositionné, ce que la même règle autorise explicitement.
*/
.fiche__demand:focus-visible[b-ymj4wtc7a5] {
    outline-offset: calc(-1 * var(--focus-ring-width));
}

/*
  Pas de règle de focus ici, et c'est voulu : l'anneau est défini une seule fois
  dans app.css pour tout élément interactif. La carte mène à plusieurs endroits
  depuis A4, donc chacun garde l'anneau à sa place plutôt qu'un anneau posé sur
  la carte entière, qui ne dirait pas laquelle est visée.
*/

@media (prefers-reduced-motion: no-preference) {
    .fiche__demand[b-ymj4wtc7a5],
    .fiche__market[b-ymj4wtc7a5] {
        transition: background-color var(--duration-base) var(--ease-out);
    }
}

/*
  L'ACTION N'APPARAÎT QU'AU POINTEUR (013, FR-014). « Il faut juste faire
  apparaître le bouton de panier, pas toutes ces infos » (le propriétaire,
  2026-10-05) : une liste de cent cartes au repos ne montre pas cent boutons.

  La question est celle du POINTEUR, pas de la largeur : c'est la même requête
  que les bulles de la 009, et elle n'ajoute donc pas de point de rupture. Là où
  il n'y a pas de survol - un doigt - la bande reste dessinée en permanence, en
  pied de carte comme n'importe quel autre élément : il n'y a aucun pointeur à
  attendre, donc rien à révéler. Tout ce qui suit ne concerne que la souris.
*/
@media (hover: hover) and (pointer: fine) {
    /*
      LA CARTE S'ALLONGE ET SE SOULÈVE (amendement A3 du 2026-10-07, direction
      A3 choisie sur maquette).

      Elle gardait la place de la bande en permanence, en transparence, pour que
      la grille ne bouge pas sous le pointeur. L'intention était bonne et le prix
      trop cher : « le bouton panier apparaît sur un espace qui est vide », sous
      chacune des cent sept cartes, tout le temps.

      Le marché de CSFloat, que le propriétaire a montré, ne paie pas ce prix :
      sa carte grandit PAR-DESSUS ses voisines. Rien ne bouge non plus, et il n'y
      a pas de trou. C'est ce qui est fait ici - « il ajoutait des pixels à la
      carte », mot pour mot.

      Au repos la carte s'arrête donc à ses places de marché. Au survol la bande
      sort par le bas, hors du flux, et la carte se soulève de 4 px en grandissant
      de 3,5 %. Sa case dans la grille ne change pas : la rangée du dessous ne
      recule pas, elle passe dessous.
    */
    .fiche[b-ymj4wtc7a5] {
        position: relative;
        /* Il remplace le découpage de la carte, qui ne peut plus l'être : la
           bande vit désormais en dehors d'elle. */
        overflow: visible;
        transition:
            transform var(--duration-base) var(--ease-out),
            box-shadow var(--duration-base) var(--ease-out);
    }

    /* Le socle portait ses angles droits parce que la carte les rognait. Elle ne
       rogne plus rien, donc il porte les siens. */
    .fiche__plinth[b-ymj4wtc7a5] {
        border-start-start-radius: calc(var(--radius-md) - var(--border-width));
        border-start-end-radius: calc(var(--radius-md) - var(--border-width));
        overflow: hidden;
    }

    .fiche .fiche__demand[b-ymj4wtc7a5] {
        position: absolute;
        /* Débordante d'un trait de chaque côté : elle couvre la bordure de la
           carte au lieu de laisser deux filets dépasser sous elle. */
        inset-inline: calc(-1 * var(--border-width));
        inset-block-start: 100%;
        border: var(--border-width) solid var(--color-edge);
        border-block-start: var(--skin-rarity-rule) solid var(--rarity);
        border-end-start-radius: var(--radius-md);
        border-end-end-radius: var(--radius-md);
        /* OPAQUE. Elle flotte au-dessus de la rangée suivante : un lavis posé
           sur du transparent laisserait lire la carte d'en dessous au travers. */
        background-color: color-mix(in srgb, var(--rarity) var(--skin-demand-mix), var(--color-surface));
        box-shadow: var(--shadow-md);
        opacity: 0;
        pointer-events: none;
    }

    .fiche__demand:hover[b-ymj4wtc7a5] {
        background-color: color-mix(in srgb, var(--rarity) var(--skin-demand-mix-hover), var(--color-surface));
    }

    /* La bande d'une fiche au panier flotte au-dessus de la rangée suivante
       comme les autres : son lavis à l'accent doit donc être opaque lui aussi,
       sinon on lit la carte d'en dessous au travers. */
    .fiche[data-in-cart="true"] .fiche__demand[b-ymj4wtc7a5] {
        background-color: var(--color-accent-quiet-solid);
    }

    /*
      :focus-within autant que :hover (FR-017). Révélée par le survol seul, la
      bande serait invisible à la tabulation : on la déclencherait sans l'avoir
      vue.

      OPACITY ET NON VISIBILITY, et c'est un test qui l'a dit : visibility:
      hidden retire l'élément du parcours de tabulation, donc :focus-within ne
      pouvait plus jamais se déclencher. pointer-events tient l'autre moitié -
      transparente, la bande ne doit pas être une cible cliquable invisible.

      PLUS data-in-cart : une carte au panier gardait sa bande dessinée en
      permanence, ce qui ferait maintenant pendre une bande sous chaque objet
      retenu, par-dessus la rangée suivante. Au repos, c'est la bordure à
      l'accent qui dit qu'elle est prise (FR-004) - elle le disait déjà.
    */
    .fiche:hover[b-ymj4wtc7a5],
    .fiche:focus-within[b-ymj4wtc7a5] {
        z-index: 1;
        transform: translateY(calc(-1 * var(--skin-lift))) scale(var(--skin-lift-scale));
        box-shadow: var(--shadow-card-hover);
    }

    .fiche:hover .fiche__demand[b-ymj4wtc7a5],
    .fiche:focus-within .fiche__demand[b-ymj4wtc7a5] {
        opacity: 1;
        pointer-events: auto;
    }

    @media (prefers-reduced-motion: no-preference) {
        .fiche__demand[b-ymj4wtc7a5] {
            transition:
                opacity var(--duration-fast) var(--ease-out),
                transform var(--duration-base) var(--ease-out);
            transform: translateY(calc(-1 * var(--space-2)));
        }

        .fiche:hover .fiche__demand[b-ymj4wtc7a5],
        .fiche:focus-within .fiche__demand[b-ymj4wtc7a5] {
            transform: translateY(0);
        }
    }

    /* Le mouvement est l'explication, pas la fonction : sans lui la bande
       apparaît simplement, et la carte ne bouge plus du tout. */
    @media (prefers-reduced-motion: reduce) {
        .fiche[b-ymj4wtc7a5] {
            transition: none;
        }

        .fiche:hover[b-ymj4wtc7a5],
        .fiche:focus-within[b-ymj4wtc7a5] {
            transform: none;
        }
    }
}
/* /Components/Skins/SkinPager.razor.rz.scp.css */
/*
  La pagination (012) : deux liens et une position, sur une ligne qui revient à
  la ligne quand elle manque de place.
*/

.pager[b-oqs28f8w2g] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-3) var(--space-4);
}

.pager__link[b-oqs28f8w2g] {
    display: inline-flex;
    align-items: center;
    min-block-size: var(--tap-target-min);
    padding: 0 var(--space-4);
    border-radius: var(--radius-chip);
    background-color: var(--color-plane);
    color: var(--color-text);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    text-decoration: none;
}

.pager__link:hover[b-oqs28f8w2g] {
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
}

.pager__position[b-oqs28f8w2g] {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}
/* /Components/Skins/SkinToolbar.razor.rz.scp.css */
/*
  La barre au-dessus des fiches (012, amendements A3 et A5).

  Posée, pas encadrée : elle ne mène nulle part par elle-même, elle règle la
  vue. UNE SEULE RANGÉE depuis le 2026-10-05 - elle en prenait trois, et le
  propriétaire : « filtre sur plusieurs lignes, pas top, pas esthétique ». Tout
  tient sur une ligne qui se replie d'elle-même quand la largeur manque, sans
  point de rupture.
*/

.toolbar__row[b-6qttc5jj5g] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

/* Le champ prend la place qui reste, mais ne descend pas sous une largeur où
   l'on ne lit plus ce qu'on tape ; sous cette largeur la rangée se replie. */
.toolbar__search[b-6qttc5jj5g] {
    display: flex;
    flex: 1 1 14rem;
    gap: var(--space-2);
    min-inline-size: 0;
}

.toolbar__box[b-6qttc5jj5g] {
    position: relative;
    display: flex;
    flex: 1;
    min-inline-size: 0;
}

.toolbar__glyph[b-6qttc5jj5g] {
    inline-size: var(--search-glyph-size);
    block-size: var(--search-glyph-size);
}

.toolbar__glyph--search[b-6qttc5jj5g] {
    position: absolute;
    inset-inline-start: var(--space-4);
    inset-block-start: 50%;
    transform: translateY(-50%);
    color: var(--color-text-muted);
    pointer-events: none;
}

.toolbar__field[b-6qttc5jj5g] {
    flex: 1;
    min-inline-size: 0;
    min-block-size: var(--tap-target-min);
    /* Room for the glass on the left and the cross on the right. */
    padding-block: 0;
    padding-inline: calc(var(--space-4) + var(--search-glyph-size) + var(--space-3)) var(--tap-target-min);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-chip);
    background-color: var(--color-plane-quiet);
    color: var(--color-text);
    font: inherit;
    font-size: var(--text-base);
    transition: background-color var(--duration-fast) var(--ease-out);
}

.toolbar__field:hover[b-6qttc5jj5g],
.toolbar__field:focus[b-6qttc5jj5g] {
    background-color: var(--color-plane);
}

.toolbar__field[b-6qttc5jj5g]::placeholder {
    color: var(--color-text-muted);
}

/* Une seule croix : celle du navigateur s'ajoutait à la nôtre (le propriétaire,
   sur la maquette). */
.toolbar__field[b-6qttc5jj5g]::-webkit-search-cancel-button {
    appearance: none;
    display: none;
}

.toolbar__clear[b-6qttc5jj5g] {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    display: grid;
    place-items: center;
    inline-size: var(--tap-target-min);
    border-radius: var(--radius-full);
    color: var(--color-text-muted);
    transition: color var(--duration-fast) var(--ease-out);
}

.toolbar__clear[hidden][b-6qttc5jj5g] {
    display: none;
}

.toolbar__clear:hover[b-6qttc5jj5g] {
    color: var(--color-text);
}

.toolbar__submit[b-6qttc5jj5g] {
    min-block-size: var(--tap-target-min);
    padding: 0 var(--space-4);
    border: 0;
    border-radius: var(--radius-chip);
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    cursor: pointer;
}

/* The list follows what is typed once site.js has taken the catalog over: the
   button would only repeat it. The attribute is set on the catalog (Skins.razor)
   by the script, and never by the server. */
[data-skins-live] .toolbar__submit[b-6qttc5jj5g] {
    display: none;
}

/* ---------------------------------------------------------------------------
   Les types, en segments collés
   ---------------------------------------------------------------------------
   Collés et non séparés, parce que c'est UN choix et non quatre interrupteurs :
   la forme dit laquelle des deux mécaniques on a sous les doigts. Les tris, eux,
   se cumulent - ils restent des pastilles séparées, dans le dépliant.
*/
.toolbar__kinds[b-6qttc5jj5g] {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-chip);
    background-color: var(--color-plane-quiet);
    overflow: hidden;
}

.toolbar__segment[b-6qttc5jj5g] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--tap-target-min);
    padding: 0 var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    text-decoration: none;
    transition:
        background-color var(--duration-fast) var(--ease-out),
        color var(--duration-fast) var(--ease-out);
}

.toolbar__segment:hover[b-6qttc5jj5g] {
    background-color: var(--color-plane);
    color: var(--color-text);
}

.toolbar__segment[aria-current="true"][b-6qttc5jj5g] {
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
    font-weight: var(--weight-bold);
}

/* ---------------------------------------------------------------------------
   Le tri, dans un dépliant
   ---------------------------------------------------------------------------
   Un <details> et non un menu : les clés se cumulent depuis A3, et un menu
   déroulant ne sait pas dire « rareté puis float ». Celui-ci s'ouvre sans
   script, et son résumé porte l'ordre courant pour qu'on le lise sans l'ouvrir.
*/
.toolbar__sort[b-6qttc5jj5g] {
    position: relative;
}

.toolbar__summary[b-6qttc5jj5g] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--tap-target-min);
    padding: 0 var(--space-3);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-chip);
    background-color: var(--color-plane-quiet);
    color: var(--color-text);
    font-size: var(--text-sm);
    cursor: pointer;
    list-style: none;
    white-space: nowrap;
}

/* Le triangle par défaut de Safari, qui ignore list-style. */
.toolbar__summary[b-6qttc5jj5g]::-webkit-details-marker {
    display: none;
}

.toolbar__summary[b-6qttc5jj5g]::after {
    content: "⌄";
    color: var(--color-text-muted);
}

.toolbar__summary:hover[b-6qttc5jj5g] {
    background-color: var(--color-plane);
}

/* Ouvert, le panneau flotte au-dessus de la liste plutôt que de la pousser :
   sans cela, ouvrir le tri déplacerait toutes les fiches. */
.toolbar__panel[b-6qttc5jj5g] {
    position: absolute;
    z-index: 1;
    inset-block-start: calc(100% + var(--space-1));
    /* Accroché au BORD DE DÉBUT : sur téléphone la rangée se replie et le
       dépliant commence à gauche de la surface - accroché à la fin, le panneau
       sortait de l'écran par la gauche. Et borné, pour que les pastilles se
       replient plutôt que de pousser le panneau hors du cadre. */
    inset-inline-start: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    inline-size: max-content;
    max-inline-size: min(18rem, calc(100vw - 2 * var(--space-4)));
    padding: var(--space-3);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-raised);
    box-shadow: var(--shadow-md);
}

.toolbar__label[b-6qttc5jj5g] {
    margin-inline-end: var(--space-1);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.toolbar__chips[b-6qttc5jj5g] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Une pastille par clé, à la taille d'une cible tactile ; celles de l'ordre
   courant prennent l'accent. */
.toolbar__chip[b-6qttc5jj5g] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--tap-target-min);
    padding: 0 var(--space-3);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-chip);
    background-color: var(--color-plane-quiet);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    text-decoration: none;
    transition:
        background-color var(--duration-fast) var(--ease-out),
        color var(--duration-fast) var(--ease-out);
}

.toolbar__chip:hover[b-6qttc5jj5g] {
    background-color: var(--color-plane);
    color: var(--color-text);
}

.toolbar__chip[aria-current="true"][b-6qttc5jj5g] {
    border-color: color-mix(in srgb, var(--color-accent) var(--chip-active-edge-mix), transparent);
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
    font-weight: var(--weight-bold);
}

/* Combien il y a derrière la pastille : une pastille vaut un clic seulement si
   elle mène quelque part, et le nombre le dit avant le clic. */
.toolbar__tally[b-6qttc5jj5g] {
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    opacity: var(--tally-opacity);
}

.toolbar__rank[b-6qttc5jj5g] {
    display: inline-grid;
    place-items: center;
    min-inline-size: var(--sort-rank-size);
    block-size: var(--sort-rank-size);
    border-radius: var(--radius-full);
    background-color: var(--color-accent);
    color: var(--color-text-on-accent);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
}

.toolbar__direction[b-6qttc5jj5g] {
    font-size: var(--text-xs);
}

.toolbar__reset[b-6qttc5jj5g] {
    display: inline-flex;
    align-items: center;
    min-block-size: var(--tap-target-min);
    padding-inline: var(--space-2);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

.toolbar__reset:hover[b-6qttc5jj5g] {
    color: var(--color-text);
}

.toolbar__count[b-6qttc5jj5g] {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

/*
  Au-dessus de 64rem, la rangée se resserre : « c'est trop gros » (2026-10-05),
  dit devant un écran de bureau. En dessous, chaque contrôle garde ses 44 px -
  c'est là qu'un doigt les vise, et une cible rétablie par une zone invisible
  ne tenait pas : celle des segments était découpée par l'overflow du groupe,
  et un <input> ne peut pas en porter du tout.
*/
@media (min-width: 64rem) {
    .toolbar__field[b-6qttc5jj5g],
    .toolbar__submit[b-6qttc5jj5g],
    .toolbar__segment[b-6qttc5jj5g],
    .toolbar__summary[b-6qttc5jj5g] {
        min-block-size: var(--toolbar-control-compact);
    }
}

@media (prefers-reduced-motion: reduce) {
    .toolbar__field[b-6qttc5jj5g],
    .toolbar__clear[b-6qttc5jj5g],
    .toolbar__chip[b-6qttc5jj5g] {
        transition: none;
    }
}
/* /Components/Social/SocialLinkCard.razor.rz.scp.css */
/*
  The social tile.

  It grows with the room available: this is what fills the frame (FR-010), and
  the only way to do it without inventing content, which FR-014 forbids. The
  content spreads between top and bottom rather than floating in the middle of
  a large void.

  Its colour comes from its brand, through a data attribute. The wash, the top
  rule and the icon plate carry it; the body text stays white, because a
  description in brand colour would fail contrast on half of the four.
*/

.tile[b-say7na4ivk] {
    --brand: var(--brand-default);
    --brand-text: var(--brand-default-text);

    position: relative;
    display: flex;
    flex-direction: column;
    /*
      Le contenu part du haut, et ne se centre plus (retouche du 2026-09-12,
      vue sur la préprod).

      Le propriétaire : « les cartes de réseaux sont inégales, on peut avoir le
      logo qui touche la bordure du haut ou pas selon le texte de présentation ;
      l'intérieur n'est pas forcément à la même hauteur entre les cartes ». La
      cause est exactement là : la rangée a une hauteur fixe, le contenu était
      centré dedans, donc une description de trois lignes poussait la pastille
      vers le filet de marque et une description d'une ligne la laissait au
      milieu. Ancré en haut, chaque tuile a la même géométrie intérieure, quelle
      que soit la longueur de sa phrase.
    */
    justify-content: flex-start;
    align-items: flex-start;
    /* Deux colonnes sur téléphone depuis l'amendement A9, donc une tuile plus
       basse et plus serrée : c'est ce qui ramène le bandeau « À la une » dans le
       premier écran. Les valeurs d'origine reviennent à 40rem. */
    gap: var(--space-3);
    min-block-size: var(--tile-min-height-compact);
    block-size: 100%;
    padding: var(--space-4);
    border: var(--border-width) solid var(--color-border-strong);
    border-radius: var(--radius-tile);
    background-color: var(--color-surface);
    background-image: radial-gradient(
        120% 120% at 100% 0%,
        color-mix(in srgb, var(--brand) var(--tile-wash-mix), transparent),
        transparent 55%
    );
    color: var(--color-text);
    text-decoration: none;
    overflow: hidden;
    transition:
        transform var(--duration-base) var(--ease-out),
        border-color var(--duration-base) var(--ease-out);
}

.tile[data-brand="twitch"][b-say7na4ivk] {
    --brand: var(--brand-twitch);
    --brand-text: var(--brand-twitch-text);
}

.tile[data-brand="discord"][b-say7na4ivk] {
    --brand: var(--brand-discord);
    --brand-text: var(--brand-discord-text);
}

.tile[data-brand="x"][b-say7na4ivk] {
    --brand: var(--brand-x);
    --brand-text: var(--brand-x-text);
}

.tile[data-brand="csgorep"][b-say7na4ivk] {
    --brand: var(--brand-csgorep);
    --brand-text: var(--brand-csgorep-text);
}

/* The hard rule at the top: the brand is seen before the name is read. */
.tile[b-say7na4ivk]::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    block-size: var(--focus-ring-width);
    background-color: var(--brand);
}

/*
  FR-016: two properties change on hover, not one. The lift alone is barely
  noticeable on a dark ground; the border alone is invisible out of the corner
  of the eye.
*/
.tile:hover[b-say7na4ivk] {
    transform: translateY(calc(-1 * var(--lift-hover)));
    border-color: var(--brand);
}

/*
  Le filigrane (amendement A9 du 2026-09-12).

  Posé dans le coin bas-droit, débordant hors de la tuile : un logo entier au
  centre serait un motif, un logo coupé par le bord est une texture. Sous le
  texte dans l'ordre de peinture, et sans jamais capter le pointeur.

  Son opacité est un jeton : c'est la seule valeur de ce filigrane, elle a été
  réglée à l'œil sur le prototype, et elle vaut pour les quatre tuiles.
*/
.tile__mark[b-say7na4ivk] {
    position: absolute;
    inset-block-end: calc(-1 * var(--space-4));
    inset-inline-end: calc(-1 * var(--space-4));
    z-index: 0;
    inline-size: var(--watermark-size);
    block-size: var(--watermark-size);
    object-fit: contain;
    opacity: var(--watermark-opacity);
    pointer-events: none;
}

/* Tout le contenu passe devant le filigrane. */
.tile__icon[b-say7na4ivk],
.tile__body[b-say7na4ivk] {
    position: relative;
    z-index: 1;
}

.tile__icon[b-say7na4ivk] {
    display: grid;
    place-items: center;
    flex: none;
    inline-size: var(--icon-size-tile);
    block-size: var(--icon-size-tile);
    border-radius: var(--radius-tile);
    background-color: color-mix(in srgb, var(--brand) 20%, var(--color-bg));
}

.tile__glyph[b-say7na4ivk] {
    inline-size: var(--icon-size);
    block-size: var(--icon-size);
    object-fit: contain;
}

.tile__body[b-say7na4ivk] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    /* Lets a long description wrap rather than widening the tile and
       pushing the grid out of the frame. */
    min-inline-size: 0;
}

.tile__label[b-say7na4ivk] {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wide);
    color: var(--brand-text);
}

/*
  La phrase réserve sa hauteur, et c'est ce qui rend les quatre tuiles égales
  (retouche du 2026-09-12, et sa revue).

  Les rangées de la grille sont dimensionnées chacune pour elle-même - il faut
  qu'elles le soient, sinon elles s'étirent vers le cadre - donc l'égalité doit
  venir du contenu : si les quatre tuiles demandent la même hauteur, les rangées
  tombent à la même valeur, à deux colonnes comme à quatre.

  Quatre lignes sur téléphone, où une tuile fait 180px de large et où la phrase
  de Discord en prend quatre ; trois au-delà de 40rem. Aucune phrase n'est
  coupée - c'est un plancher, pas un plafond, et le test des tuiles vérifie que
  chacune tient entière.
*/
.tile__description[b-say7na4ivk] {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    min-block-size: calc(4 * var(--text-sm) * var(--leading-normal));
}

@media (min-width: 40rem) {
    .tile__description[b-say7na4ivk] {
        min-block-size: calc(3 * var(--text-sm) * var(--leading-normal));
    }
}

/*
  Above 64rem the tile takes all the height the surface gives it (FR-010). A
  3rem icon floated in it; enlarging the icon gives the tile the visual weight
  that goes with its size, without adding a word of content.
*/
@media (min-width: 40rem) {
    .tile[b-say7na4ivk] {
        gap: var(--space-4);
        min-block-size: var(--tile-min-height);
        padding: var(--space-5);
    }
}

@media (min-width: 64rem) {
    .tile__icon[b-say7na4ivk] {
        inline-size: var(--icon-size-tile-wide);
        block-size: var(--icon-size-tile-wide);
    }

    .tile__glyph[b-say7na4ivk] {
        inline-size: var(--icon-size-lg);
        block-size: var(--icon-size-lg);
    }
}
/* /Components/Social/SocialLinksGrid.razor.rz.scp.css */
/*
  DEUX COLONNES, PUIS QUATRE À 64REM (amendement A9 du 2026-09-12, qui amende la
  FR-011 de la 001).

  C'était une colonne, puis deux à partir de 40rem. Sur un téléphone, les quatre
  tuiles empilées faisaient 600px : le bandeau « À la une » commençait à 1 142px
  du haut, soit à un écran et demi de défilement, et le test du premier écran
  (SC-001 de la 011) l'a montré. Deux colonnes ramènent les tuiles à 300px et le
  bandeau dans le premier écran.

  Quatre sur un grand écran, comme sur le prototype que le propriétaire a
  validé : une seule rangée, et la page commence par une ligne de destinations
  plutôt que par un bloc de quatre.

  Toujours deux seuils, 40rem et 64rem : les deux que ce projet s'autorise.

  The tracks stretch, and that is what fills the frame (FR-010): the surface
  puts this grid in a "1fr" row, the grid is stretched into it, it shares its
  height between its rows, and the tiles take it. No content is invented to
  occupy the space - it is the same four links, larger.

  No "block-size: 100%" here: the stretch already comes from the row, and
  removing the declaration changes nothing on screen. Verified by deleting it,
  not assumed.
*/
.grid[b-6w80qfq5kl] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: minmax(var(--tile-min-height-compact), max-content);
    gap: var(--space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

@media (min-width: 40rem) {
    .grid[b-6w80qfq5kl] {
        gap: var(--space-4);
        /*
          La rangée a un plancher, et plus une hauteur fixe (retouche du
          2026-09-12, vue sur la préprod).

          Le propriétaire : « les cartes de réseaux sont inégales, on peut avoir
          le logo qui touche la bordure du haut ». La cause n'était pas
          seulement l'alignement : à 12rem fixes, la tuile Discord - la seule
          dont la phrase prend trois lignes - dépassait sa rangée, et
          `overflow: hidden` coupait « parties. » tout en poussant la pastille
          contre le filet de marque. Une valeur fixe qui ne tient pas son
          contenu est une valeur fausse.

          minmax(plancher, max-content) : chaque rangée vaut son contenu, et
          jamais le cadre - c'est « 1fr » qui avait donné des tuiles de 400px
          pour trois lignes de texte, et un essai de le réintroduire ici a de
          nouveau étiré une rangée à 277px dans la pile de l'onglet.

          L'égalité entre rangées ne vient donc pas de la grille mais du
          contenu : la phrase d'une tuile réserve sa hauteur (voir
          `SocialLinkCard.razor.css`), donc les quatre tuiles demandent la même
          chose et les rangées tombent à la même valeur. La revue du 2026-09-12
          a trouvé l'inégalité que cela corrige : 195px en haut, 192px en bas,
          à deux colonnes.
        */
        grid-auto-rows: minmax(var(--tile-height), max-content);
    }
}

@media (min-width: 64rem) {
    .grid[b-6w80qfq5kl] {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/*
  display:grid rather than flex, on purpose: a lone grid child stretches into
  its cell by default, so each tile takes the full height of its row without
  this sheet having to reach into SocialLinkCard with ::deep - which an
  isolated sheet cannot do.
*/
.grid__item[b-6w80qfq5kl] {
    display: grid;
    min-inline-size: 0;
}
/* /Components/Twitch/BroadcastList.razor.rz.scp.css */
/*
  Deux formes, une par largeur (amendement A7 du 2026-09-12).

  Sur téléphone : des lignes compactes - l'image à gauche, fondue vers la
  droite, le texte à côté. Trois rediffusions tiennent alors dans un écran au
  lieu d'un écran et demi, et le propriétaire les a validées ainsi : « ok pour
  les rediff compact ».

  À partir de 40rem : des cartes, image dessus et texte dessous, de la même
  famille que le bandeau et les clips. Les lignes pleine largeur dénotaient
  entre le bandeau et la grille de clips - « les rediffusions dénotent » -, et
  ce qui les rattache aux autres est le fondu de l'image dans la carte, pas un
  cadre de plus.

  L'image se fond par un masque et jamais par un calque de couleur : un calque
  suppose la couleur derrière lui (research R4).
*/
.broadcasts[b-7lr41m28vc] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.broadcasts__link[b-7lr41m28vc] {
    display: flex;
    align-items: stretch;
    min-height: var(--tap-target-min);
    overflow: hidden;
    color: inherit;
    text-decoration: none;
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
}

@media (prefers-reduced-motion: no-preference) {
    .broadcasts__link[b-7lr41m28vc] {
        transition: transform var(--duration-base) var(--ease-out),
                    box-shadow var(--duration-base) var(--ease-out);
    }
}

.broadcasts__link:hover[b-7lr41m28vc] {
    transform: translateY(calc(-1 * var(--lift-hover)));
    box-shadow: var(--shadow-card-hover);
}

/*
  The focus ring is drawn, never removed. It is the only thing a keyboard user
  has to tell where they are (SC-015).
*/
.broadcasts__link:focus-visible[b-7lr41m28vc] {
    outline: var(--focus-ring-width) solid var(--color-focus);
    outline-offset: var(--focus-ring-offset);
}

/*
  The image reserves its own box before it loads: a fixed width and a declared
  ratio, matching the width/height attributes on the element. An entry without a
  thumbnail simply has no box - it does not get a grey placeholder, which would
  be a hole drawn deliberately (FR-011).
*/
.broadcasts__thumb[b-7lr41m28vc] {
    flex: none;
    /*
      Une part de la ligne, jamais une largeur fixe : à 320px les 10rem fixes
      d'origine laissaient 149px de débordement horizontal, ce que SC-012
      interdit. Le défaut ne s'est vu qu'une fois l'image chargée - sans image,
      pas de boîte à déborder.
    */
    width: var(--thumb-inline-size-compact);
    /* Le fondu vers le texte, à droite en forme compacte (A7). */
    mask-image: linear-gradient(to right, #000 var(--image-fade-stop), transparent);
    /*
      Without this the width and height attributes win. They are presentational
      hints, so author CSS beats them - but only for the property it sets, and
      nothing here was setting height. The result was a 160x180 box: the CSS
      width applied, the attribute height applied, and aspect-ratio does not
      override an explicit height.

      The attributes still do their real job, which is telling the browser the
      ratio to reserve before the image arrives (SC-008). They are not there to
      size it.
    */
    height: auto;
    aspect-ratio: var(--thumb-ratio);
    object-fit: cover;
}

/*
  Le cadre de l'image absente (A7) : la forme de la carte, sans image. Un plan
  plein plutôt qu'une boîte bordée - il porte la carte, il ne se présente pas
  comme un contenu.
*/
.broadcasts__thumb--absent[b-7lr41m28vc] {
    display: block;
    background-color: var(--color-plane);
}

.broadcasts__body[b-7lr41m28vc] {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-1);
    /* Le texte remonte dans le fondu de l'image plutôt que de commencer après
       une bande vide. */
    margin-inline-start: calc(-1 * var(--space-5));
    padding: var(--space-3) var(--space-4) var(--space-3) 0;
    min-width: 0;
}

/* The two facts that tell two entries apart come first, and read as one line. */
.broadcasts__meta[b-7lr41m28vc] {
    display: flex;
    /* The date and the duration are what tell two entries apart, so they wrap
       onto a second line rather than being truncated on a narrow screen. */
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: baseline;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--color-text);
}

/*
  Titles repeat - four of the five real ones are identical - so a long one is
  truncated rather than allowed to wrap into three lines that say the same thing
  as the entry above.
*/
.broadcasts__title[b-7lr41m28vc] {
    overflow: hidden;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.broadcasts__views[b-7lr41m28vc] {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/*
  "Nouveau", where a view count of zero would otherwise sit.

  It uses the accent on its own rather than a filled pill: the four link cards
  above already carry saturated colour, and a bright chip here would pull the eye
  away from them for a word that is only context. The accent on the page
  background is the pair feature 001 measured for contrast; nothing new to check.
*/
.broadcasts__badge[b-7lr41m28vc] {
    align-self: start;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    letter-spacing: var(--tracking-wide);
    color: var(--color-accent);
    text-transform: uppercase;
}

/*
  À partir de 40rem, la forme carte (A7) : autant de colonnes que la largeur en
  porte, l'image dessus et fondue vers le bas, le texte dessous.
*/
@media (min-width: 40rem) {
    .broadcasts[b-7lr41m28vc] {
        /*
          auto-fill, jamais auto-fit, et la capture de la porte G4 l'a tranché :
          hors ligne la liste ne montre que deux rediffusions, et auto-fit
          replie les colonnes vides - les deux cartes prenaient alors la moitié
          de l'onglet chacune, deux fois la taille d'un clip juste au-dessus.
          auto-fill garde les colonnes que la largeur permet, donc une carte de
          rediffusion fait la taille d'une carte de clip.

          La largeur plancher est un jeton (--broadcast-card-min) : c'est une
          valeur de style, et aucune ne s'écrit ici.
        */
        grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--broadcast-card-min)), 1fr));
        gap: var(--space-4);
    }

    .broadcasts__link[b-7lr41m28vc] {
        flex-direction: column;
        /* Toutes les cartes d'une rangée à la même hauteur : sans ça, une carte
           au titre court est plus basse que sa voisine et la rangée se lit
           comme un défaut. */
        block-size: 100%;
    }

    .broadcasts__thumb[b-7lr41m28vc] {
        width: 100%;
        mask-image: linear-gradient(to bottom, #000 var(--image-fade-stop), transparent);
    }

    .broadcasts__body[b-7lr41m28vc] {
        justify-content: flex-start;
        margin-inline-start: 0;
        margin-block-start: calc(-1 * var(--space-5));
        padding: 0 var(--space-4) var(--space-4);
    }
}
/* /Components/Twitch/ClipGrid.razor.rz.scp.css */
/*
  FR-003c: whole rows only, so the grid never ends on a gap.

  auto-fit was the first version and it is what produced the defect the owner
  saw: the column count moved freely with the width, so a fixed four clips
  landed as 2x2 here and 4x1 there, and any width that gave three columns would
  have left a hole.

  The count is fixed at each of the project's two thresholds instead, and the
  number of clips shown follows it: two columns and two clips, then three and
  three, then four and four. One full row at every size.

  One row rather than two since 2026-09-07, at the owner's word: « on devrait en
  afficher moins tout en gardant le minimum pour afficher une ligne complète ».
  The rule that produced it is untouched - only the number of rows it is applied
  to changed.

  The surplus is hidden rather than cut server-side, because only the browser
  knows how wide it is - and a hidden lazy image is never fetched, so a phone
  downloads two thumbnails and not four.
*/
/*
  minmax(0, 1fr) and not 1fr, which is minmax(auto, 1fr).

  A track's automatic minimum is the min-content of what it holds, and a clip's
  author is an unbreakable token: "par un_autre_viewer" cannot wrap, so the track
  refuses to shrink below it and the grid grows past its column. On Windows the
  name happened to fit; on Linux the same string is wider and 320px overflowed by
  22px - green here, red on the CI, which is the whole reason that gate exists.

  Zeroing the minimum lets the track take its share, and the wrap rule below lets
  the word give way rather than the layout.
*/
.clips[b-04jhczni53] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.clips__item[b-04jhczni53] {
    display: block;
    min-width: 0;
}

.clips__item:nth-child(n + 3)[b-04jhczni53] {
    display: none;
}

@media (min-width: 40rem) {
    .clips[b-04jhczni53] {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .clips__item:nth-child(-n + 3)[b-04jhczni53] {
        display: block;
    }

    .clips__item:nth-child(n + 4)[b-04jhczni53] {
        display: none;
    }
}

@media (min-width: 64rem) {
    .clips[b-04jhczni53] {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }

    .clips__item:nth-child(-n + 4)[b-04jhczni53] {
        display: block;
    }
}

/*
  La carte d'un clip (amendement A7 du 2026-09-12).

  Même famille que le bandeau : une arête, une profondeur, et l'image qui se
  fond dans la carte au lieu de s'y arrêter net. Le propriétaire, sur le
  prototype : « la délimitation entre l'image et le reste est peut-être un peu
  nette ? contrairement à la une ».

  Pas de rembourrage en haut ni sur les côtés de l'image : elle va jusqu'aux
  bords, et c'est le masque qui la termine.
*/
.clips__link[b-04jhczni53] {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
    color: inherit;
    text-decoration: none;
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
}

@media (prefers-reduced-motion: no-preference) {
    .clips__link[b-04jhczni53] {
        transition: transform var(--duration-base) var(--ease-out),
                    box-shadow var(--duration-base) var(--ease-out);
    }
}

.clips__link:hover[b-04jhczni53] {
    transform: translateY(calc(-1 * var(--lift-hover)));
    box-shadow: var(--shadow-card-hover);
}

.clips__link:focus-visible[b-04jhczni53] {
    outline: var(--focus-ring-width) solid var(--color-focus);
    outline-offset: var(--focus-ring-offset);
}

/*
  The image and the length that sits on it.

  height:auto on the thumbnail is what lets the ratio hold: the width and height
  attributes are presentational hints, and without it the attribute height wins
  over aspect-ratio. That cost a render on the broadcasts before it was
  understood.
*/
.clips__media[b-04jhczni53] {
    position: relative;
    display: block;
}

.clips__thumb[b-04jhczni53] {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: var(--thumb-ratio);
    object-fit: cover;
    /* Le fondu vers la carte (A7), au même point que le bandeau et les
       rediffusions : c'est un jeton, pas un réglage local. */
    mask-image: linear-gradient(to bottom, #000 var(--image-fade-stop), transparent);
}

/*
  A clip with no usable image keeps the tile's shape, so the grid does not jump
  and the row stays aligned. Not a grey box drawn for its own sake: it is the
  frame the duration badge sits in, which is the one thing a clip always has
  (FR-011).
*/
.clips__thumb--absent[b-04jhczni53] {
    background-color: var(--color-surface-raised);
}

/*
  The length, on the image (FR-003c).

  It carries its own opaque background rather than relying on the thumbnail
  behind it: a text laid on an arbitrary photo has no contrast that can be
  measured, and SC-011 asks for the real pair. --color-bg against --color-text is
  the pair this site has measured everywhere else.
*/
.clips__duration[b-04jhczni53] {
    position: absolute;
    right: var(--space-2);
    bottom: var(--space-2);
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    line-height: var(--leading-tight);
    color: var(--color-text);
    background-color: var(--color-bg);
    border-radius: var(--radius-sm);
}

.clips__body[b-04jhczni53] {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: var(--space-1);
    /* Le texte remonte dans le fondu de l'image (A7). */
    margin-block-start: calc(-1 * var(--space-4));
    padding: 0 var(--space-3) var(--space-3);
    min-width: 0;
}

/*
  Une ligne de titre, et plus deux (amendement A7 du 2026-09-12).

  Deux lignes réservées alignaient les tuiles entre elles - c'était le sens de
  la FR-003c de la 004 -, mais sur la carte à image fondue elles laissaient une
  bande vide sous la plupart des titres : le propriétaire, sur le prototype,
  « beaucoup d'espace vide sur les clips ». Une ligne pour tous aligne tout
  aussi bien, puisque la hauteur est la même pour chaque carte, et la ligne
  « clippé par » qui arrive en dessous porte maintenant le reste du texte.
*/
.clips__title[b-04jhczni53] {
    display: -webkit-box;
    overflow: hidden;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    line-height: var(--leading-tight);
    color: var(--color-text);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
}

/*
  Date, views and author on one line, pinned to the bottom of the card so the
  whole row's metadata sits on the same baseline.

  A viewer's name is one token and nothing in it can wrap, so on a 160px tile it
  is the thing most likely to push the layout wider than its column. Breaking
  inside the word is the lesser evil: a name cut across two lines is still
  readable, a page that scrolls sideways is not (SC-012).
*/
.clips__meta[b-04jhczni53] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: auto;
    overflow-wrap: anywhere;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/*
  « clippé par Vengi » (amendement A8 du 2026-09-12).

  Sur chaque clip, et pas seulement sur ceux d'un spectateur : le propriétaire,
  « j'avoue que je trouve que l'auteur est important pour le côté
  communautaire ». Ceux de la chaîne portent le nom de la chaîne plutôt que
  rien, sinon la ligne manque une fois sur deux et les cartes redeviennent
  inégales.

  Sa propre ligne, sous la méta : un nom de spectateur est un mot insécable, et
  le mettre en bout de la ligne des chiffres est ce qui poussait la colonne trop
  large sur Linux (SC-012).
*/
.clips__by[b-04jhczni53] {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    overflow-wrap: anywhere;
}

.clips__by b[b-04jhczni53] {
    font-weight: var(--weight-medium);
    color: var(--color-text);
}
/* /Components/Twitch/ModuleShell.razor.rz.scp.css */
/*
  The frame only. Everything inside the loaded state belongs to the module that
  supplied it, and this sheet deliberately does not reach in: no ::deep, so a
  module's own stylesheet stays the one place its entries are described.

  No breakpoint here either. The heading and the note read the same at 320px and
  at 1440px, and the two thresholds this project allows itself are spent where
  they change something.
*/
.module[b-t2mrpjfa7q] {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.module__title[b-t2mrpjfa7q] {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-medium);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-wide);
    color: var(--color-text);
}

/*
  The heading as a link. It keeps the heading's own colour and weight - an
  underlined blue title would read as a stray link rather than as the section's
  name - and earns its underline on hover and focus, where a pointer or a
  keyboard is asking whether it leads anywhere.
*/
/*
  The heading as a link, and nothing beside it (FR-002a).

  It keeps the heading's own colour and weight - an underlined blue title would
  read as a stray link rather than as the section's name - and earns its
  underline on hover and focus, where a pointer or a keyboard is asking whether
  it leads anywhere.

  There was an arrow here. The owner asked for it to go and for nothing to
  replace it, which is the shorter answer to the same question.
*/
.module__title-link[b-t2mrpjfa7q] {
    color: inherit;
    text-decoration: none;
}

.module__title-link:hover[b-t2mrpjfa7q] {
    text-decoration: underline;
    text-underline-offset: var(--space-1);
}

.module__title-link:focus-visible[b-t2mrpjfa7q] {
    outline: var(--focus-ring-width) solid var(--color-focus);
    outline-offset: var(--focus-ring-offset);
}

/*
  The empty and unavailable sentence. Muted rather than dimmed by opacity:
  --color-text-muted is a token whose contrast against --color-bg was checked,
  whereas an opacity would produce a colour nobody has measured (SC-011).
*/
.module__note[b-t2mrpjfa7q] {
    margin: 0;
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
}
/* /Components/Twitch/Spotlight.razor.rz.scp.css */
/*
  Le bandeau « À la une » (011).

  La famille de cartes du lot : une arête, un plan, une profondeur, et l'image
  qui se fond dans la carte au lieu de s'y arrêter net. Le propriétaire, sur le
  prototype : « la délimitation entre l'image et le reste est peut-être un peu
  nette ? contrairement a la une » - c'est ce fondu-là qui est repris partout
  ailleurs.

  Le fondu est un masque, pas un calque de couleur posé par-dessus : un calque
  suppose la couleur du fond et se voit dès qu'elle change, un masque retire
  l'image et laisse voir ce qu'il y a derrière, quoi que ce soit (research R4).
*/

.spot-block[b-r9au30qseq] {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* Le titre calme, aux jetons du cadre des modules - c'est le choix A du
   2026-09-11 : « le titre du bandeau crie un peu ». */
.spot__heading[b-r9au30qseq] {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-medium);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-wide);
    color: var(--color-text);
}

.spot[b-r9au30qseq] {
    display: grid;
    /* Une colonne sur téléphone : l'image dessus, le texte dessous (FR-018). */
    grid-template-columns: 1fr;
    overflow: hidden;
    border: var(--border-width) solid var(--color-edge);
    border-radius: var(--radius-card);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
    color: var(--color-text);
    text-decoration: none;
}

.spot__image[b-r9au30qseq] {
    /* 16/9, la forme de la source (FR-006) : la boîte ne rogne rien. */
    inline-size: 100%;
    block-size: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    /* Le fondu vers la carte. Le point de départ est un jeton, le même dans les
       trois composants du lot. */
    mask-image: linear-gradient(to bottom, #000 var(--image-fade-stop), transparent);
}

.spot__body[b-r9au30qseq] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    /* L'image finit en transparence : le texte remonte dans son fondu plutôt
       que de commencer après une bande vide. */
    margin-block-start: calc(-1 * var(--space-5));
    padding: var(--space-4);
    min-inline-size: 0;
}

.spot__label[b-r9au30qseq] {
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-chip);
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
}

/* En direct, l'étiquette prend la seule couleur de la page autorisée à crier -
   celle du bloc de statut, pour que les deux disent la même chose. */
.spot[data-subject="live"] .spot__label[b-r9au30qseq] {
    background-color: var(--live-emphasis);
    color: var(--live-on-block);
}

.spot__title[b-r9au30qseq] {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    /* Deux lignes au plus, quelle que soit la longueur du titre (FR-019) : un
       titre de direct peut faire cent quarante caractères. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

.spot__meta[b-r9au30qseq] {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

/* L'habitude : posée, pas mise en avant. C'est un repère, pas un rendez-vous. */
.spot__habit[b-r9au30qseq] {
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-chip);
    background-color: var(--color-plane);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

.spot__cta[b-r9au30qseq] {
    margin-block-start: var(--space-1);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-chip);
    background-color: var(--color-plane);
    color: var(--color-text);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
}

/* Le survol et le focus portent sur la carte entière, puisque la carte entière
   est le lien (FR-002). Le mouvement est enveloppé : SC de la 002 sur la
   motion réduite. */
@media (prefers-reduced-motion: no-preference) {
    .spot[b-r9au30qseq] {
        transition:
            transform var(--duration-base) var(--ease-out),
            box-shadow var(--duration-base) var(--ease-out);
    }
}

.spot:hover[b-r9au30qseq] {
    transform: translateY(calc(-1 * var(--lift-hover)));
    box-shadow: var(--shadow-card-hover);
}

.spot:hover .spot__cta[b-r9au30qseq] {
    background-color: var(--color-accent-quiet);
    color: var(--color-accent);
}

.spot:focus-visible[b-r9au30qseq] {
    outline: var(--focus-ring-width) solid var(--color-focus);
    outline-offset: var(--focus-ring-offset);
}

/*
  À partir de 40rem, le texte passe à côté de l'image (FR-018) : l'image prend
  un peu moins de la moitié, et le fondu tourne vers la droite.
*/
@media (min-width: 40rem) {
    .spot[b-r9au30qseq] {
        grid-template-columns: minmax(0, 3fr) minmax(0, 4fr);
        align-items: center;
    }

    .spot__image[b-r9au30qseq] {
        block-size: 100%;
        mask-image: linear-gradient(to right, #000 var(--image-fade-stop), transparent);
    }

    /*
      Sans image, une seule colonne (capture de la porte G4).

      Twitch ne fabrique la miniature d'une rediffusion qu'après coup, et c'est
      la plus récente - donc celle du bandeau - qui en est privée le jour d'un
      direct. En deux colonnes, le texte se retrouvait à 40 % de la largeur avec
      une colonne vide à sa gauche. L'état « pas d'image » est un état, et il a
      sa propre mise en page plutôt qu'un trou.
    */
    .spot[data-image="absent"][b-r9au30qseq] {
        grid-template-columns: minmax(0, 1fr);
    }

    .spot[data-image="absent"] .spot__body[b-r9au30qseq] {
        padding-inline-start: var(--space-5);
    }

    /*
      Pas de décalage négatif ici, et la capture de la porte G4 est la raison :
      le texte remontait dans le fondu de l'image, mais son propre rembourrage
      intérieur était plus petit que le décalage, donc le titre commençait huit
      pixels hors de la carte et « TYLOO » s'affichait « YLOO ». Sur une colonne
      le texte a sa place ; c'est le fondu de l'image qui les relie.
    */
    .spot__body[b-r9au30qseq] {
        margin-block-start: 0;
        padding: var(--space-5) var(--space-5) var(--space-5) 0;
    }
}
/* /Components/Twitch/StreamRhythm.razor.rz.scp.css */
/*
  Une section ouverte, pas une carte (amendement A5 du 2026-09-12).

  Elle était une carte parce que le bloc au-dessus en était une, et deux blocs
  de la même colonne doivent appartenir à la même famille. Ils y appartiennent
  toujours : c'est la famille qui a changé. Le propriétaire, sur la colonne de
  gauche - « j'ai l'impression qu'on est un peu too much en mode carte ? » -, et
  la règle qui en sort : une carte veut dire un clic. La Régularité ne se clique
  pas, donc elle n'est plus une boîte : un filet la sépare de la section du
  dessus, et rien d'autre.
*/
.rhythm[b-0392esnn62] {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    /* De l'air des deux côtés du filet (retouche du 2026-09-12, vue sur la
       préprod) : « les lignes entre les "cartes" un peu collé ». */
    padding: var(--space-5) 0 var(--space-4);
    border-block-start: var(--border-width) solid var(--color-rule);
}

/*
  A kicker rather than a heading: an h2 for a screen reader and for the document
  outline, drawn at the size the card above draws its own label. A surface-sized
  heading in a 300px rail shouts.
*/
.rhythm__kicker[b-0392esnn62] {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--color-text-muted);
}

/*
  The answer, in French, where the figure used to be (FR-010j).

  Two sizes on one line rather than two lines: the lead carries the weight, the
  rest of the sentence continues it in the reading size. Splitting them would
  make a headline and a caption out of one sentence.
*/
.rhythm__sentence[b-0392esnn62] {
    margin: 0;
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
    text-wrap: pretty;
}

.rhythm__sentence strong[b-0392esnn62] {
    font-size: var(--text-base);
    font-weight: var(--weight-bold);
    color: var(--color-text);
}

/*
  A state, not an error: centred in the card so it reads as something intended
  rather than as content that failed to arrive.
*/
.rhythm__note[b-0392esnn62] {
    margin: 0;
    text-align: center;
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

/*
  The week, one line per day (010), in the owner's pick of 2026-09-11 (B+3):
  StreamsCharts' clock - "16:00 – 03:30", the slot centred, the length in a
  tag - with a thread of the frise under each line for colour and shape.

  One set of columns for all seven lines: the list is the grid, each line a
  subgrid of it, so the day column is as wide as the longest day and the
  slots line up from one line to the next. The first attempt gave every line
  its own fixed tracks, and a long slot ran left over its day in a 275px card
  (FR-009a) - a track sized by its content cannot be overrun by it, and the
  slot's track is never narrower than the slot.

  In the display face, like the FaceIT card's figures: it is the one face the
  site ships, so the week is the same width on every machine. In the system
  face it was not - DejaVu on the Linux CI ran a Sunday slot two pixels over
  its day where Segoe UI left room, and a visitor on Linux would have seen
  it.
*/
.schedule[b-0392esnn62] {
    font-family: var(--font-display);
    display: grid;
    grid-template-columns: max-content minmax(max-content, 1fr) max-content;
    column-gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.schedule__row[b-0392esnn62] {
    grid-column: 1 / -1;
    display: grid;
    /* Before subgrid, the same tracks on each line: every browser this site
       supports reads the second declaration, an older one keeps the first. */
    grid-template-columns: max-content minmax(max-content, 1fr) max-content;
    grid-template-columns: subgrid;
    align-items: center;
    row-gap: var(--space-1);
    position: relative;
    /* Plus de bande, donc plus de pastille à dessiner autour du texte : de
       l'air au-dessus et en dessous, et le filet au-dessus fait la séparation
       (A6). */
    padding-block: var(--space-2);
    font-size: var(--text-sm);
    line-height: var(--leading-tight);
}

/*
  Des lignes séparées par de l'air et un filet (amendement A6 du 2026-09-12).

  C'était une ligne sur deux sur une bande plus claire. Les bandes lisaient bien
  le tableau, et elles lisaient aussi « composant système » : le propriétaire,
  sur la carte entière, « Régularité dénote aussi mais je ne sais pas encore
  quoi ». Le filet fait le même travail avec un huitième de l'encre, et sept
  lignes aérées se lisent encore comme un tableau - c'est ce que les captures du
  prototype ont montré.
*/
.schedule__row + .schedule__row[b-0392esnn62] {
    border-block-start: var(--border-width) solid var(--color-rule);
}

.schedule__day[b-0392esnn62] {
    grid-column: 1;
    color: var(--color-text-muted);
}

.schedule__slot[b-0392esnn62] {
    grid-column: 2;
    justify-self: center;
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
    color: var(--color-text);
    white-space: nowrap;
}

/* The length in a small tag, StreamsCharts' "~4h 40m" box. */
.schedule__air[b-0392esnn62] {
    grid-column: 3;
    justify-self: end;
    padding: calc(var(--space-1) / 2) var(--space-1);
    border-radius: var(--radius-xs);
    background-color: var(--chip-surface);
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
    white-space: nowrap;
}

/* What is missing instead of a slot: quieter than a slot, on the length's edge. */
.schedule__none[b-0392esnn62] {
    grid-column: 2 / -1;
    justify-self: end;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    white-space: nowrap;
}

/*
  The thread of the frise (FR-003c): the evening's place between noon and six
  the next morning, the whole width of the line, midnight marked. Where the
  bar starts and how long it runs arrive as --from and --span, computed from
  the slot when the page is rendered.
*/
.schedule__thread[b-0392esnn62] {
    grid-column: 1 / -1;
    position: relative;
    display: block;
    block-size: var(--schedule-thread-height);
    border-radius: var(--radius-full);
    background-color: var(--schedule-thread-track);
}

.schedule__thread[b-0392esnn62]::before {
    content: "";
    position: absolute;
    inset-block: calc(var(--schedule-thread-height) * -1);
    inset-inline-start: var(--schedule-midnight);
    border-inline-start: var(--border-width) solid var(--color-border-strong);
}

.schedule__bar[b-0392esnn62] {
    position: absolute;
    inset-block: 0;
    inset-inline-start: var(--from);
    inline-size: var(--span);
    border-radius: var(--radius-full);
    background-color: var(--color-accent);
}

/* A start alone: a short stroke where it began, no length to draw. */
.schedule__bar--start[b-0392esnn62] {
    inline-size: var(--schedule-start-mark);
}

/* The scale, once for the seven threads, on the same edges as they are. */
.schedule__scale[b-0392esnn62] {
    position: relative;
    block-size: calc(var(--text-2xs) * var(--leading-normal));
    margin: calc(var(--space-1) * -1) var(--space-2) 0;
    font-size: var(--text-2xs);
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
}

.schedule__scale span[b-0392esnn62] {
    position: absolute;
    inset-block-start: 0;
    transform: translateX(-50%);
}

.schedule__scale span:nth-child(1)[b-0392esnn62] {
    inset-inline-start: 0;
    transform: none;
}

.schedule__scale span:nth-child(2)[b-0392esnn62] {
    inset-inline-start: calc(100% / 3);
}

.schedule__scale span:nth-child(3)[b-0392esnn62] {
    inset-inline-start: var(--schedule-midnight);
}

.schedule__scale span:nth-child(4)[b-0392esnn62] {
    inset-inline-start: 100%;
    transform: translateX(-100%);
}

/* The most recent evening, in a line's bubble. */
.schedule__last[b-0392esnn62] {
    display: block;
    font-size: var(--text-xs);
}

/*
  Below 64rem the card was the two sentences alone (FR-010b1 of 005): the dots
  and the ribbon would have pushed the tabs down by a whole card. The week is
  text, seven short lines, and it is the answer a phone visitor came for, so it
  is shown at every width (010, FR-009).

  The state note is not: on a phone the card is shown only when it has an
  answer, and the modifier says when it has none (FR-010b3).
*/
.rhythm__note[b-0392esnn62],
.rhythm--silent[b-0392esnn62] {
    display: none;
}

@media (min-width: 64rem) {
    .rhythm--silent[b-0392esnn62] {
        display: flex;
    }

    .rhythm__note[b-0392esnn62] {
        display: block;
    }
}
