/* Blog article — redesign

   Extracted from the redesign mockup (Blog/quand-les-taupes-sont-elles-les-plus-actives.html) and scoped to the blog: the site
   chrome (navbar, footer, buttons) is deliberately left out — the front already
   owns it, and re-importing it would fight the existing cascade.

   The design tokens are redeclared under .blog-redesign rather than :root so
   they cannot leak into the rest of the site, which runs on its own palette.
*/


@media (max-width: 1024px) {
    .article-layout { grid-template-columns: 1fr; }
    .article-sidebar { position: static;
                display: grid;
                grid-template-columns: 1fr 1fr;
                gap: 16px; }
    .toc-links-wrapper { max-height: 220px; }
}

@media (max-width: 768px) {
    .article-hero { padding: 120px 20px 0; }
    .article-hero h1 { font-size: 1.6rem; }
    .article-hero-meta { gap: 12px;
                font-size: 0.82rem; }
    /* Clamping the height against width:100% has nothing to say about the
       aspect ratio, so the browser simply stretched the picture — a square
       cover came out 350x260. Cropping is the honest reading of a fixed-height
       band. */
    .article-cover img { max-height: 260px;
                object-fit: cover; }
    .article-layout { padding: 32px 16px 60px; }
    .article-sidebar { grid-template-columns: 1fr; }
    .toc-links-wrapper { max-height: 230px;
                scrollbar-width: none;
                -ms-overflow-style: none; }
    .toc-links-wrapper::-webkit-scrollbar { display: none; }
    .sidebar-toc a { padding: 10px 0;
                padding-left: 12px;
                min-height: 42px;
                display: flex;
                align-items: center; }
}

@media (max-width: 480px) {
    .article-hero { padding: 100px 16px 0; }
    .article-content h2 { font-size: 1.3rem; }
    .article-content p { font-size: 0.98rem; }
    .toc-links-wrapper { max-height: 200px; }
}

.article-info-box.warning h4 i {
    color: #f0980c;
}

.article-info-box.tip {
    background: #f0faf6;
            border-left: 4px solid var(--green);
}

.article-info-box.tip h4 i {
    color: var(--green-dark);
}

.article-info-box.summary {
    background: #f0f4ff;
            border-left: 4px solid #4a7cff;
}

.article-info-box.summary h4 i {
    color: #4a7cff;
}

.article-info-box.cold {
    background: #f0f8ff;
            border-left: 4px solid #5ba8d6;
}

.article-info-box.cold h4 i {
    color: #5ba8d6;
}

.urgency-badge i {
    font-size: 1rem;
}

.article-hero::before {
    content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: radial-gradient(ellipse at 50% 0%, rgba(255, 107, 53, 0.08) 0%, transparent 60%);
            pointer-events: none;
}

.article-breadcrumb {
    display: inline-flex;
            align-items: center;
            gap: 8px;
            color: var(--gray);
            font-size: 0.85rem;
            animation: fadeIn 0.8s ease forwards;
}

.article-breadcrumb a {
    color: var(--gray);
            transition: color 0.2s ease;
}

.article-breadcrumb a:hover {
    color: var(--orange);
}

.article-breadcrumb .separator {
    color: rgba(0, 0, 0, 0.25);
}

.article-hero h1 {
    font-size: clamp(1.8rem, 4vw, 2.8rem);
            font-weight: 900;
            color: var(--text-dark);
            line-height: 1.2;
            margin-bottom: 12px;
            animation: slideUp 1s ease forwards;
            animation-delay: 0.2s;
            opacity: 0;
}

.article-hero-subtitle {
    font-size: clamp(0.95rem, 1.5vw, 1.1rem);
            color: var(--text-dark);
            font-weight: 400;
            line-height: 1.5;
            margin-bottom: 24px;
            animation: slideUp 1s ease forwards;
            animation-delay: 0.3s;
            opacity: 0;
}

/* The catchphrase is a rich-text field, and back when this slot had no
   typography of its own the editors gave it some by hand: seven of the
   twenty-five articles wrap the whole sentence in <em>, nine set
   text-align:center. So the same slot rendered italic on one article and roman
   on the next, for no editorial reason.

   The hero owns the typography now — and it is already centred, which makes the
   inline alignment a no-op. Only the italic still shows, so only the italic is
   neutralised, and the standfirst matches the mockup on every article.

   The <em> stays in the markup: a screen reader still stresses the whole
   sentence, which is not what emphasis means. That is a content fix, in the BO,
   not something CSS should paper over. */
.article-hero-subtitle em,
.article-hero-subtitle i {
    font-style: normal;
}

/* The last paragraph's own bottom margin doubles up with the block's. */
.article-hero-subtitle p:last-child {
    margin-bottom: 0;
}

.article-hero-meta {
    display: flex;
            align-items: center;
            justify-content: center;
            gap: 20px;
            flex-wrap: wrap;
            color: var(--gray);
            font-size: 0.9rem;
            animation: slideUp 1s ease forwards;
            animation-delay: 0.4s;
            opacity: 0;
}

.article-hero-meta span {
    display: inline-flex;
            align-items: center;
            gap: 6px;
}

/* The mockup's byline: orange icons against grey text, with the author's name
   the only thing in it set in the ink.

   Written for .vd-icon and not for the mockup's <i>, which is a FontAwesome
   glyph — the icons here are inline SVG, so the rule as copied matched nothing.
   These are stroked, not filled, which is why the colour goes on `stroke` as
   well: base.css sets stroke: currentColor for every blog icon, and a `color`
   alone would leave them painting the inherited grey. */
.article-hero-meta .vd-icon {
    color: var(--orange);
    stroke: var(--orange);
}

.article-hero-meta .author-name {
    font-weight: 600;
            color: var(--text-dark);
}

/* 24px above, 12px below: the caption belongs to the image, so it sits closer
   to it than the next paragraph does. Symmetric margins put the two at the same
   distance and left the caption reading as loose prose.

   This rule is duplicated further down the file — the mockup's stylesheet was
   extracted twice — so both copies carry the same value; changing one alone
   would let the other win. */
.article-info-box h4 {
    display: flex;
            align-items: center;
            gap: 8px;
            font-size: 1rem;
            font-weight: 700;
            color: var(--text-dark);
            margin-bottom: 12px;
}

.article-info-box h4 i {
    color: var(--orange);
}

.article-info-box p,
        .article-info-box li {
    font-size: 0.95rem;
            color: var(--text-body);
}

.article-video-section h3 {
    font-size: 1.15rem;
            font-weight: 700;
            color: var(--text-dark);
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            gap: 10px;
}

.article-video-section h3 i {
    color: #ff0000;
            font-size: 1.3rem;
}

.article-video-wrapper {
    position: relative;
            width: 100%;
            padding-bottom: 56.25%;
            border-radius: 12px;
            overflow: hidden;
            background: #000;
}

.article-video-wrapper iframe {
    position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            border: none;
}

.article-products-inline h3 {
    font-size: 1.15rem;
            font-weight: 700;
            color: var(--text-dark);
            margin-bottom: 20px;
            display: flex;
            align-items: center;
            gap: 10px;
}

.article-products-inline h3 i {
    color: var(--orange);
}

.inline-product-grid {
    display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
}

.inline-product-card {
    display: flex;
            gap: 16px;
            background: var(--gray-light);
            border-radius: 16px;
            padding: 16px;
            transition: all 0.3s ease;
            align-items: center;
            /* Anchors the name link's stretched overlay to the card. Nothing
               between the two may be positioned, or the overlay stops at it. */
            position: relative;
}

.inline-product-card:hover {
    transform: translateY(-3px);
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
}

.inline-product-card-image {
    width: 110px;
            height: 110px;
            border-radius: 12px;
            overflow: hidden;
            flex-shrink: 0;
            background: var(--white);
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 8px;
}

.inline-product-card-image img {
    max-width: 100%;
            max-height: 100%;
            object-fit: contain;
}

.inline-product-card-info .inline-product-desc {
    font-size: 0.82rem;
            color: var(--gray);
            line-height: 1.5;
            margin-bottom: 8px;
}

.inline-product-card-info .inline-product-bottom {
    display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 10px;
}

.inline-product-card-info .inline-product-price {
    font-size: 1.05rem;
            font-weight: 800;
            color: var(--orange);
}

.inline-product-card-info .inline-product-btn {
    display: inline-flex;
            align-items: center;
            gap: 6px;
            background: var(--orange);
            color: var(--white);
            padding: 6px 16px;
            border-radius: 50px;
            font-size: 0.78rem;
            font-weight: 600;
            transition: all 0.2s ease;
            white-space: nowrap;
}

.inline-product-card-info .inline-product-btn:hover {
    background: var(--orange-light);
}

.article-product-cta h3 {
    color: var(--white);
            font-size: 1.2rem;
            font-weight: 700;
            margin-bottom: 8px;
}

.article-product-cta p {
    color: rgba(255, 255, 255, 0.6);
            font-size: 0.95rem;
            margin-bottom: 20px;
}

.article-product-cta .cta-btn {
    display: inline-flex;
            align-items: center;
            gap: 8px;
            background: var(--orange);
            color: var(--white);
            padding: 12px 28px;
            border-radius: 50px;
            font-weight: 600;
            font-size: 0.95rem;
            transition: all 0.2s ease;
}

.article-product-cta .cta-btn:hover {
    background: var(--orange-light);
            transform: translateY(-2px);
            box-shadow: 0 4px 15px rgba(255, 107, 53, 0.3);
}

.sidebar-toc {
    background: var(--gray-light);
            border-radius: 16px;
            padding: 24px;
            margin-bottom: 12px;
            position: relative;
}

.sidebar-toc h2 {
    font-size: 0.95rem;
            font-weight: 700;
            color: var(--text-dark);
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            gap: 8px;
}

.sidebar-toc h2 i {
    color: var(--orange);
            font-size: 0.85rem;
}

.toc-links-wrapper {
    max-height: 280px;
            overflow-y: auto;
            scrollbar-width: thin;
            scrollbar-color: rgba(255, 107, 53, 0.3) transparent;
            /* The bottom fade lives HERE, on the scrolling box, not on the panel.
               As .sidebar-toc::after it was positioned against the nav, so it
               landed on the panel's own 24px of bottom padding and faded nothing:
               the list still ended on a hard cut, which reads as "that is all
               there is" rather than "there is more". A mask is painted in the
               box's own space, so it stays put while the content scrolls under
               it. */
            -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent);
            mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent);
}

/* Nothing left to reveal: the fade would otherwise dim the last entry for good. */
.sidebar-toc.scrolled-to-bottom .toc-links-wrapper {
    -webkit-mask-image: none;
    mask-image: none;
}

.toc-links-wrapper::-webkit-scrollbar {
    width: 4px;
}

.toc-links-wrapper::-webkit-scrollbar-track {
    background: transparent;
}

.toc-links-wrapper::-webkit-scrollbar-thumb {
    background: rgba(255, 107, 53, 0.3);
            border-radius: 4px;
}

.toc-scroll-hint {
    /* inline-flex, not the plain centred text it was: the arrow has to sit on
       the text's baseline row rather than wrap to its own line. */
    display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
            /* 0.72rem in the mockup — too small for the one line whose job is to
               be noticed. */
            font-size: 0.8rem;
            color: var(--gray);
            padding-top: 8px;
            transition: opacity 0.3s ease;
}

.toc-scroll-hint .vd-icon {
    color: var(--orange);
    flex-shrink: 0;
}

.sidebar-toc.scrolled-to-bottom .toc-scroll-hint {
    opacity: 0;
}

.sidebar-toc a {
    display: block;
            color: var(--gray);
            font-size: 0.88rem;
            padding: 8px 0;
            padding-left: 12px;
            border-left: 2px solid transparent;
            transition: all 0.2s ease;
}

.sidebar-toc a:hover,
        .sidebar-toc a.active {
    color: var(--orange);
            border-left-color: var(--orange);
            padding-left: 16px;
}

.sidebar-share {
    background: var(--gray-light);
            border-radius: 16px;
            padding: 16px 24px;
            margin-bottom: 12px;
            display: flex;
            align-items: center;
            gap: 16px;
}

.sidebar-share h2 {
    font-size: 0.95rem;
            font-weight: 700;
            color: var(--text-dark);
            white-space: nowrap;
}

.share-buttons {
    display: flex;
            gap: 10px;
}

.share-btn {
    width: 42px;
            height: 42px;
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--white);
            font-size: 1rem;
            transition: all 0.3s ease;
}

.share-btn:hover {
    transform: translateY(-3px);
}

.share-btn.facebook {
    background: #1877f2;
}

.share-btn.twitter {
    background: #1da1f2;
}

.share-btn.whatsapp {
    background: #25d366;
}

.share-btn.copy {
    background: var(--navy);
            color: rgba(255, 255, 255, 0.7);
}

.sidebar-related {
    background: var(--gray-light);
            border-radius: 16px;
            padding: 24px;
}

.sidebar-related h2 {
    font-size: 0.95rem;
            font-weight: 700;
            color: var(--text-dark);
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            gap: 8px;
}

.sidebar-related h2 i {
    color: var(--orange);
            font-size: 0.85rem;
}

.sidebar-product-item {
    display: flex;
            gap: 12px;
            padding: 10px 0;
            border-bottom: 1px solid rgba(0, 0, 0, 0.06);
            transition: all 0.2s ease;
}

.sidebar-product-item:last-child {
    border-bottom: none;
}

.sidebar-product-item:hover {
    padding-left: 4px;
}

.sidebar-product-img {
    width: 56px;
            height: 56px;
            border-radius: 10px;
            overflow: hidden;
            flex-shrink: 0;
            background: var(--white);
}

.sidebar-product-img img {
    width: 100%;
            height: 100%;
            object-fit: cover;
}

.sidebar-product-info h5 {
    font-size: 0.85rem;
            font-weight: 600;
            color: var(--text-dark);
            line-height: 1.4;
            margin-bottom: 2px;
            transition: color 0.2s ease;
}

.sidebar-product-item:hover .sidebar-product-info h5 {
    color: var(--orange);
}

.sidebar-product-stars {
    display: flex;
            gap: 2px;
            margin-bottom: 2px;
}

.sidebar-product-stars i {
    font-size: 0.65rem;
            color: #f5a623;
}

.sidebar-product-price {
    font-size: 0.82rem;
            font-weight: 700;
            color: var(--orange);
}

.sidebar-products-btn {
    display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            width: 100%;
            margin-top: 14px;
            padding: 10px 16px;
            background: var(--orange);
            color: var(--white);
            font-family: 'Poppins', sans-serif;
            font-size: 0.82rem;
            font-weight: 600;
            border-radius: 10px;
            transition: all 0.2s ease;
}

.sidebar-products-btn:hover {
    background: var(--orange-light);
            transform: translateY(-1px);
}

.sidebar-products-btn i {
    font-size: 0.75rem;
            transition: transform 0.2s ease;
}

.sidebar-products-btn:hover i {
    transform: translateX(3px);
}

.article-tags span.label {
    font-weight: 600;
            font-size: 0.9rem;
            color: var(--text-dark);
}

.article-tag {
    display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 5px 14px;
            border-radius: 50px;
            background: var(--gray-light);
            color: var(--gray);
            font-size: 0.82rem;
            font-weight: 500;
            transition: all 0.2s ease;
}

.article-tag:hover {
    background: rgba(255, 107, 53, 0.1);
            color: var(--orange);
}

/* Layout shell and prose typography, taken from the mockup rule by rule.
   Extracted by selector rather than by line range: the first attempt cut
   .product-card-btn:hover and .article-sidebar::-webkit-scrollbar in half, and
   the unbalanced braces made the parser swallow everything after them —
   including the @keyframes, which left the article's own h1 at opacity 0. */
.article-content {
    max-width: 740px;
}

.article-content h2 {
    font-size: 1.6rem;
    font-weight: 800;
    color: var(--text-dark);
    margin: 28px 0 0;
    padding-top: 10px;
}

/* :not() rather than an override further down. The card title is an <h3> and
   this rule is 0,1,1 against the component's 0,1,0, so it was repainting it at
   20px with a 24px margin where the category page draws 14.7px and none. Excluding
   it here lets product_card_v2.css apply untouched — restating its values would
   have duplicated a component this file does not own. */
.article-content h3:not(.product-card-v2-title) {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--text-dark);
    margin: 24px 0 10px;
}

.article-content h4 {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--text-dark);
    margin: 24px 0 10px;
}

.article-content p {
    font-size: 1.05rem;
    line-height: 1.8;
    color: var(--text-body);
    margin-bottom: 20px;
}

.article-content ul, .article-content ol {
    padding-left: 24px;
    margin-bottom: 20px;
}

.article-content ul {
    list-style: none;
    padding-left: 0;
}

.article-content ul li {
    position: relative;
    /* 24px in the mockup. The dot is 8px wide and sits at left: 0, so that left
       16px between the dot and the text — wider than the space between the
       words it introduces. 20px brings it to 12px. */
    padding-left: 20px;
    /* 10px in the mockup, on top of a 1.75 line-height: an item that wraps to
       two lines then sat almost as far from the next item as from its own
       second line, and the list stopped reading as one block. The line-height
       already separates the items; this only has to keep them from touching. */
    margin-bottom: 2px;
    font-size: 1.02rem;
    line-height: 1.7;
    color: var(--text-body);
}

.article-content ul li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 10px;
    width: 8px;
    height: 8px;
    background: var(--orange);
    border-radius: 50%;
}

.article-content ol li {
    /* Same tightening as the bulleted list above, so the two read alike. */
    margin-bottom: 2px;
    font-size: 1.02rem;
    line-height: 1.7;
    color: var(--text-body);
}

.article-content a {
    color: var(--orange);
    font-weight: 500;
    transition: color 0.2s ease;
}

.article-content a:hover {
    color: var(--orange-light);
}

.article-content blockquote {
    border-left: 4px solid var(--orange);
    background: rgba(255, 107, 53, 0.05);
    padding: 20px 24px;
    margin: 24px 0;
    border-radius: 0 12px 12px 0;
    font-style: italic;
    color: var(--text-body);
    font-size: 1.05rem;
    line-height: 1.7;
}

/* The second copy of the rule above. Same value, deliberately.

   16px on top, not 24: the h2 above no longer carries a bottom margin, so this
   is now the whole distance between a section title and the picture opening it.
   Enough to read as a new block, short enough that the picture still belongs to
   the title rather than floating between two sections. */
.article-content img {
    border-radius: 12px;
    /* Longhands rather than the shorthand: the captioned-figure rule further
       down zeroes margin-bottom alone, and a shorthand here reads as if it were
       fighting it. It is not — that rule is 0,2,3 against this 0,1,1 — but the
       two are easier to hold in the head written the same way. */
    margin-top: 16px;
    margin-right: 0;
    margin-bottom: 12px;
    margin-left: 0;
    width: 100%;
    height: auto;
}

.article-sidebar::-webkit-scrollbar {
    display: none;
}


/* The keyframes the hero and cover animations reference. Without them the
   `animation: ... forwards` rules never run and every animated element stays at
   opacity 0 — the article's own title included. */
@keyframes slideUp {
    from { opacity: 0; transform: translateY(40px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* The three structural rules the extractor kept losing, written out rather than
   scraped. They are the layout itself, so a silent miss is not survivable: the
   sidebar collapses under the prose and the article reads as one long column. */
.article-layout {
    display: grid;
    grid-template-columns: 1fr 380px;
    gap: 32px;
    max-width: 1220px;
    margin: 0 auto;
    padding: 32px 20px 48px;
}

/* The two closing sections, outside the two-column band: the prose and its
   sidebar end together, and these run the full width beneath them. Same
   1220px measure so their edges line up with the column above. */
.article-closing {
    max-width: 1220px;
    margin: 0 auto;
    padding: 0 20px 64px;
}

.article-sidebar {
    position: sticky;
    top: 100px;
    align-self: start;
    max-height: calc(100vh - 120px);
    overflow-y: auto;
}

.article-cover {
    /* 900 rather than 1220: the sources are 680-1200px wide, so a wider band
       could only be filled by upscaling. This one a real cover can span. */
    max-width: 900px;
    margin: 0 auto;
}

.article-cover img {
    /* The band is 900 wide and the cover fills it, height following the
       source's own ratio. Between the two extremes tried: at 1220 a square
       cover ran past the fold and the article opened on a wall of photograph;
       bounded at 440 tall it was whole but small and floated in the row. */
    width: 100%;
    height: auto;
    border-radius: 0 0 16px 16px;
    display: block;
}

/* An article shows EITHER a cover image or a cover video, in the same slot, so
   the two are shaped alike. The player's own 8px is for a video sitting in the
   prose, which is a smaller box. */
.article-cover .blog-video {
    margin: 0;
}

.article-cover lite-youtube {
    border-radius: 0 0 16px 16px;
}

/* Below the desktop composition the cover was the only thing touching the
   screen edge — 0px against the 20px the prose keeps and the 16px the hero
   keeps — so a video read as stuck to the sides.

   Full bleed is the deliberate look on a wide screen, where the 1220px cap
   already leaves a margin. Once the layout is one column it has nothing to be
   flush WITH, so it takes the prose's own gutter and all four corners round:
   the bottom-only radius exists to join a cover that runs edge to edge under
   the hero, and an inset box that keeps it looks cut off at the top. */
@media (max-width: 992px) {
    .article-cover {
        padding: 0 20px;
        /* Same 20px on three sides. Flush against the hero is right for a cover
           that bleeds off the screen; once it is an inset, rounded box it reads
           as tucked under the hero rather than sitting below it. */
        margin-top: 20px;
    }

    .article-cover img,
    .article-cover lite-youtube {
        border-radius: 16px;
    }
}

@media (max-width: 992px) {
    .article-layout {
        grid-template-columns: 1fr;
    }

    /* Sticky is meaningless once the sidebar is stacked under the article, and
       the capped height would then clip the summary. */
    .article-sidebar {
        position: static;
        max-height: none;
        /* The aside's own box goes away so its three panels become items of the
           article layout, which is the only way to order the summary against the
           prose while the products and the share buttons stay after it. */
        display: contents;
    }

    /* Stacked, the summary landed 32px BELOW the last line of the article: a
       table of contents you find once you have read everything it indexes. It
       goes first — which is also where the editorial "Dans cet article" lists
       have always put theirs. */
    .sidebar-toc {
        order: -1;
        margin-bottom: 24px;
    }

    /* These two belong after what they refer to. */
    .sidebar-related,
    .sidebar-share {
        order: 1;
        margin-top: 24px;
    }
}

/* --- Overrides against the legacy blog.css, which is still loaded ---------
   .article-content img is width:100% in the mockup, and core/css/blog.css
   styles .figcaption and .product for the old layout. Both sheets apply here,
   so these rules exist to settle the conflicts the new markup creates. */


/* The legacy sheet paints a black bar with white text on the figcaption, which
   read as a redaction mark once the images sat on a light background. */
.article-content .figcaption,
.article-content figcaption {
    background: none;
    color: var(--gray);
    font-size: 0.85rem;
    text-align: center;
    /* No padding of its own — the image's 12px bottom margin is the whole gap.
       With padding on top of that the caption text sat 22px from the picture and
       16px from the paragraph below, so it read as belonging to the wrong one. */
    padding: 0;
    margin: 0;

    /* Inline-flex so the icon and the line sit on one baseline and the pair
       stays centred under the image, whatever the caption's length. */
    display: inline-flex;
    align-items: flex-start;
    gap: 8px;
    width: 100%;
    justify-content: center;

    /* Not italic any more. It was the only thing marking this as a caption, and
       it marks nothing — it is decoration, and at 0.85rem it costs legibility
       for readers who need it most. The icon carries that meaning now. */
    font-style: normal;
}

/* The icon is a hair lower than the cap height, so it sits with the text
   rather than above it. */
.article-content figcaption .vd-icon {
    flex: none;
    margin-top: 0.2em;
    opacity: 0.75;
}

/* A caption of several lines stays readable rather than running the width of a
   full-bleed image. */
.article-content figcaption span {
    max-width: 60ch;
    text-align: left;
}

/* The product block keeps its markup — the Matomo click tracking hangs off it —
   but needs the card shape the rest of the page uses. */
.article-content .product {
    display: flex;
    align-items: center;
    gap: 16px;
    background: var(--white);
    border: 1px solid var(--gray-light);
    border-radius: 16px;
    padding: 16px;
    margin: 24px 0;
    transition: box-shadow 0.2s ease;
}

.article-content .product:hover {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

.article-content .product img {
    width: 100px;
    height: 100px;
    object-fit: contain;
    border-radius: 8px;
    margin: 0;
}

.article-content .product .content-product {
    flex: 1;
}

.article-content .product .title-product {
    font-weight: 700;
    margin: 0 0 4px;
}

.article-content .product .price-product {
    color: var(--orange);
    font-weight: 800;
    margin: 0;
}

/* Breathing room above the breadcrumb.

   The mockup writes `padding: 110px 20px 0` here, but 110 is not a design
   value: its navbar is position:fixed, so most of that padding is spent
   clearing it. Ours sits in the flow, so the same visual gap — about 32px of
   air between the navbar and the trail — is a small padding here. Copying 110
   would have left a third of the viewport empty. */
.article-hero {
    padding: 8px 20px 0;
    padding-left: max(20px, env(safe-area-inset-left));
    padding-right: max(20px, env(safe-area-inset-right));
    position: relative;
    overflow: hidden;
}

.article-hero-inner {
    max-width: 800px;
    margin: 0 auto;
    text-align: center;
    padding-bottom: 32px;
}

/* Same correction on the narrow breakpoint, which inherited the mockup's
   fixed-navbar figures (120px and 100px). No 992px override: 16px already
   suits that width, and repeating it would only be a value to keep in sync. */
@media (max-width: 600px) {
    .article-hero {
        padding: 8px 16px 0;
    }
}

/* --- Author card ------------------------------------------------------------
   No mockup equivalent, so it borrows the shapes the rest of the page uses: a
   hairline rather than a tint, the same 16px radius, the heading font on the
   name.

   Every selector here is scoped under .article-content, because the prose rule
   `.article-content img { width: 100% }` (specificity 0,1,1) otherwise beats a
   bare `.article-author-photo` (0,1,0) — which is how a 64px portrait ended up
   rendered at 690px. */
.article-content .article-author {
    display: flex;
    gap: 20px;
    align-items: flex-start;
    background: transparent;
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 16px;
    padding: 24px;
    margin: 48px 0 0;
}

.article-content .article-author-photo {
    width: 96px;
    height: 96px;
    min-width: 96px;
    border-radius: 50%;
    object-fit: cover;
    margin: 0;
    flex-shrink: 0;
}

.article-content .article-author-body {
    flex: 1;
    min-width: 0;
}

.article-content .article-author-name {
    font-family: var(--font-heading, 'Poppins', sans-serif);
    font-size: 1.05rem;
    font-weight: 800;
    color: var(--text-dark);
    margin: 0 0 2px;
}

.article-content .article-author-role {
    color: var(--text-dark);
    font-size: 0.95rem;
    font-weight: 600;
    margin: 0 0 10px;
}

/* The bio is prose, so it takes the article's own paragraph rule — size,
   leading and colour — and only its margin is reset here. Anything else would
   make the card read in a different voice from the article it closes.

   `:not([class])` matters as much as the rule itself: the name and the role are
   <p> too, and a plain `.article-author-body p` is 0,2,1 against their own
   0,2,0 rules, so it was overriding both. That is why all three were grey and
   why the role was not the colour its own rule set. Fourth time this file has
   been caught by specificity. */
.article-content .article-author-body p:not([class]) {
    margin: 0;
}

/* The agréments are the E-E-A-T signal — they read as credentials rather than
   as prose when they sit on their own line. */
.article-content .article-author-credentials {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 12px 0 0;
    padding: 0;
    list-style: none;
}

.article-content .article-author-credentials li {
    font-size: 0.78rem;
    font-weight: 600;
    padding: 4px 10px;
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 999px;
    margin: 0;
}

.article-content .article-author-credentials li::before {
    content: none;
}

@media (max-width: 600px) {
    .article-content .article-author {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }
}

/* The mini product card, inside the prose -----------------------------------
   .article-content styles every img and every a in the article, and those rules
   outrank the card's own: `.article-content img` is specificity 0,1,1 against
   `.vd-prod-img` at 0,1,0. Left alone the prose stretches the card's 60px
   thumbnail across the whole column and paints the product name orange.

   These restate the card's own values at prose specificity. They change nothing
   about how the card looks — they only stop the article from reaching into it.
   Same trap that rendered the author portrait at 690px. */
.article-content .vd-prod-img {
    width: 60px;
    height: 60px;
    margin: 0;
    border-radius: 8px;
}

.article-content .vd-prod-namelink,
.article-content .vd-prod-namelink:hover {
    color: var(--vd-ink);
    font-weight: inherit;
}

/* The vertical rhythm the old .product row had, kept so the card sits in the
   prose the same way. */
.article-content .vd-prod {
    margin: 24px 0;
}

/* The legacy `title-<position>` anchor sitting before each heading. It has no
   content and must take no space — without this it inherits .article-content's
   paragraph rhythm and opens a gap above every section title. */
.article-content .heading-anchor {
    display: block;
    height: 0;
    margin: 0;
    padding: 0;
}

/* --- The inline product card, from the mockup -------------------------------
   The wide card the article places in its prose. The mockup's own rules, with
   three departures, each forced by the card doing something the mockup's does
   not: it carries an add-to-cart.

   .article-content styles every img and every a in the article, and those rules
   outrank a bare class — `.article-content img` is 0,1,1 against
   `.inline-product-card-image img` at 0,1,0 — so the parts that meet the prose
   are restated at prose specificity. Same trap that rendered the author
   portrait at 690px. */
.article-content .inline-product-card {
    display: flex;
    gap: 16px;
    align-items: center;
    background: var(--gray-light);
    border-radius: 16px;
    padding: 16px;
    margin: 24px 0;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.article-content .inline-product-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
}

.article-content .inline-product-card-image {
    width: 110px;
    height: 110px;
    border-radius: 12px;
    overflow: hidden;
    flex-shrink: 0;
    background: var(--white);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px;
}

/* Not width:100% — the prose rule would stretch it to the column. */
.article-content .inline-product-card-image img {
    width: auto;
    max-width: 100%;
    max-height: 100%;
    margin: 0;
    border-radius: 0;
    object-fit: contain;
}

/* flex: 1, or the column sizes itself to its longest line and stops there —
   335px inside a 740px card, leaving 247px of nothing to the right of the
   button. `space-between` then distributes across that 335px instead of the
   width the card actually has. min-width: 0 lets it shrink below its content on
   a narrow screen, which a flex item does not do by default. */
.article-content .inline-product-card-info {
    flex: 1 1 auto;
    min-width: 0;
}

/* The card reads at the article's own size. It sits in the middle of the prose,
   so anything smaller reads as a footnote to the paragraph above rather than as
   a recommendation the article is making — and the mockup's 0.9rem was drawn
   for a two-column grid, where the card is half this wide. */
.article-content .inline-product-name {
    font-size: var(--vd-read);
    font-weight: 700;
    line-height: 1.4;
    margin: 0 0 6px;
}

/* The name is the card's link. It must not take the prose's orange link colour. */
.article-content .inline-product-name a {
    color: var(--text-dark);
    font-weight: 700;
    text-decoration: none;
}

/* The mockup makes the whole card one <a>, which is impossible around an
   add-to-cart <button>. The name's link stretches over the card instead: one
   link, valid markup, and the entire surface answers a click. */
.article-content .inline-product-name a::after {
    content: "";
    position: absolute;
    inset: 0;
}


.article-content .inline-product-card .review-card-stars {
    margin: 0 0 6px;
}

/* Same size as the name and as the prose, distinguished by colour and weight
   rather than by being small. */
.article-content .inline-product-desc {
    font-size: var(--vd-read);
    color: var(--gray);
    line-height: 1.5;
    margin: 0 0 10px;
}

.article-content .inline-product-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

/* The mockup writes the price in bold orange. This keeps the yellow chip the
   video card uses instead — same badge, same values, so a price reads the same
   way wherever the site shows one. It also stops the card carrying orange in
   three places at once: price, button and any link the prose puts nearby. */
.article-content .inline-product-price {
    flex: 0 0 auto;
    background: #ffe066;
    color: #232323;
    font-size: var(--vd-read);
    font-weight: 700;
    padding: 6px 14px;
    border-radius: 4px;
}

.article-content .inline-product-atc {
    margin: 0;
    flex: 0 0 auto;
    /* Above the name link's stretched overlay, or the card swallows the click
       meant for the button. Only the form is raised: the price and the
       out-of-stock notice are text, and clicking text on a card follows the
       card. */
    position: relative;
    z-index: 1;
}

.article-content .inline-product-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--orange);
    color: var(--white);
    border: 0;
    padding: 9px 18px;
    border-radius: 50px;
    /* Not var(--vd-read): a button label is interface, and 17px on a pill this
       wide crowds it. 15px keeps it legible beside the 17px text without the
       button growing to fight the price for the row. */
    font-size: 15px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.2s ease;
}

.article-content .inline-product-btn:hover {
    background: var(--orange-light);
}

/* The short label is the phone's; the media query below swaps them. */
.article-content .inline-product-btn-short {
    display: none;
}

.article-content .inline-product-btn .vd-icon {
    width: 1em;
    height: 1em;
}

.article-content .inline-product-oos {
    flex: 0 0 auto;
    color: var(--gray);
    font-weight: 700;
    font-size: 0.78rem;
    padding: 8px 14px;
    border: 1px solid #e4e7ea;
    border-radius: 50px;
    white-space: nowrap;
}

/* On a phone the card keeps the row it has on desktop, image left.

   Stacked and centred, it was 293px tall for one product, the name and the
   one-line description ran across the full column, and the image sat in a
   288x140 WHITE box on the card's grey — the product photos carry their own
   white background, so the container's white on --gray-light drew a bright
   rectangle around every one of them.

   So: the card itself turns white and takes a hairline border, which is what
   makes the photo's own white disappear instead of being outlined; the image
   container drops its background and shrinks to a thumbnail; and the
   description goes, because a paragraph is what forces the stack in the first
   place. The name, the stars, the price and the button are what the reader
   needs to decide, and they fit beside a 88px thumbnail. */
@media (max-width: 768px) {
    .article-content .inline-product-card {
        flex-direction: row;
        align-items: center;
        text-align: left;
        gap: 12px;
        padding: 12px;
        background: var(--white);
        border: 1px solid rgba(0, 0, 0, 0.08);
    }

    /* 125px is only affordable because the button drops to "Ajouter" below.
       Measured at 320px with the full label: image + a nowrap 171px button gave
       the card a 334px minimum width on a 320px screen, and it was clipped, not
       scrollable. With the short label the same card fits with room to spare. */
    .article-content .inline-product-card-image {
        width: 125px;
        height: 125px;
        padding: 0;
        background: transparent;
    }

    .article-content .inline-product-btn-full {
        display: none;
    }

    .article-content .inline-product-btn-short {
        display: inline;
    }

    .article-content .inline-product-name {
        font-size: 0.95rem;
        margin-bottom: 4px;
    }

    .article-content .inline-product-desc {
        display: none;
    }

    .article-content .inline-product-bottom {
        justify-content: flex-start;
        flex-wrap: wrap;
        gap: 8px;
    }

    .article-content .inline-product-price {
        font-size: 0.95rem;
        padding: 5px 10px;
    }

    .article-content .inline-product-btn {
        font-size: 14px;
        padding: 8px 14px;
    }
}

/* --- Ordered lists get their numbers back ----------------------------------
   redesign.css resets `dl, ol, ul { list-style: none }` for the whole site. The
   blog replaced that for <ul> with an orange dot drawn in ::before, and never
   did for <ol> — so an ordered list rendered as indented text with no order at
   all. On a list of steps ("Traitez… Appliquez… Bouchez…") the numbering IS
   part of the content, and it was silently gone.

   A counter rather than restoring list-style: decimal, for the same reason the
   bullets are drawn — it lets the marker take the accent colour and sit on the
   same 0-offset as the dot, which list-style-position cannot do. */
.article-content ol {
    list-style: none;
    counter-reset: article-step;
    padding-left: 0;
}

.article-content ol li {
    counter-increment: article-step;
    position: relative;
    padding-left: 32px;
}

.article-content ol li::before {
    content: counter(article-step) ".";
    position: absolute;
    left: 0;
    top: 0;
    min-width: 24px;
    color: var(--orange);
    font-weight: 800;
    line-height: 1.7;
}

/* A list nested inside another restarts at 1 on its own counter, which is what
   `counter-reset` on the parent already gives — but the indent has to clear the
   outer marker too. */
.article-content ol ol {
    margin-top: 10px;
    margin-bottom: 0;
}

/* --- Sidebar panels: one spacing rule, not three ---------------------------
   The three panels each carried their own margin-bottom — 12px on two of them,
   nothing on the third — so the gaps depended on which panels an article
   happened to have, and the last one before the page ended had none at all.
   The mockup separates them clearly and evenly.

   Set on the panels themselves rather than as a `gap`, because the sidebar is
   `display: contents` below 992px: its box is gone there, so a gap on it would
   have nothing to apply to. */
.article-sidebar > * + * {
    margin-top: 20px;
}

.sidebar-toc,
.sidebar-share,
.sidebar-related {
    margin-bottom: 0;
}

@media (max-width: 992px) {
    /* Stacked, the panels are separated by their own margins rather than by the
       sidebar's, which no longer exists. */
    .sidebar-share,
    .sidebar-related {
        margin-top: 20px;
    }
}

/* --- One reading size for the whole article --------------------------------
   17px / 1.75, the values the video pages read at. The article was running four
   different ones — 1.05rem at 1.8 for a paragraph, 1.02rem at 1.7 for a list
   item, another for a quote, and 0.98rem below 480px — so the voice shifted
   between a sentence and the list under it, and shrank on a phone for no
   reason. A body size is a decision about reading, not about screen width.

   Last in the file on purpose: the mockup's own rules appear twice above, and a
   media query does not outrank a later rule of equal specificity — this is what
   retires the 480px shrink as well.

   The list selectors are written out rather than a plain `.article-content li`:
   that is 0,1,1 and loses to the existing `.article-content ul li` at 0,1,2, so
   the items would have kept their old size. Matching that specificity and coming
   later is what settles it.

   Nothing is needed to protect the card or the credentials — they are interface
   rather than prose and their own rules already outrank these: the product
   description is 0,2,0 against 0,1,1, the agréments 0,2,2 against 0,1,2. */
.article-content p,
.article-content ul li,
.article-content ol li,
.article-content blockquote {
    font-size: var(--vd-read);
    line-height: 1.75;
}

/* --- Hover on the product cards, from the mockup ---------------------------
   The mockup turns the product name orange when the row is hovered, and nudges
   the row 4px right. Both say "this whole block is one link" — which matters
   here more than there, because our card holds two links and a button, so
   without it only the thing directly under the cursor reacts.

   translateX rather than the mockup's padding-left: the padding is inside a
   bordered card here and would reflow its contents on every hover. A transform
   moves the painted result and costs no layout. */
.article-sidebar .vd-prod-name,
.article-content .inline-product-name a {
    transition: color 0.2s ease;
}

.article-sidebar .vd-prod:hover .vd-prod-name,
.article-content .inline-product-card:hover .inline-product-name a {
    color: var(--orange);
}

.article-sidebar .vd-prod {
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.2s ease;
}

.article-sidebar .vd-prod:hover {
    transform: translateX(4px);
}

/* Someone who asked for less motion gets the colour and none of the movement —
   the colour is what carries the meaning. */
@media (prefers-reduced-motion: reduce) {
    .article-sidebar .vd-prod:hover,
    .article-content .inline-product-card:hover {
        transform: none;
    }
}

/* --- The author's channel link, from the mockup ----------------------------
   Scoped under .article-content like everything else in this card: the prose
   paints every <a> orange and would swallow the button's own colour. */
.article-content .article-author-links {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 14px;
}

.article-content .article-author-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    border-radius: 50px;
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
    transition: background 0.2s ease, color 0.2s ease;
}

/* YouTube red rather than the site's orange: this leaves the site, and the
   colour is the clearest way of saying so before the click. */
.article-content .article-author-link.youtube {
    background: rgba(255, 0, 0, 0.08);
    color: #d90000;
}

.article-content .article-author-link.youtube:hover {
    background: #d90000;
    color: #fff;
}

/* The glyph is a filled shape, not a stroked one — base.css strokes every blog
   icon, which would draw this one as an outline of itself. */
.article-content .article-author-link .vd-icon {
    flex: none;
    width: 1.15em;
    height: 1.15em;
    fill: currentColor;
    stroke: none;
}

/* redesign.css carries `a:hover { color: #ec7330 !important }` for the whole
   site. On a summary link that left the text in one orange and the border-left
   the rule right above sets in another — #ec7330 against #ff6b35, on the same
   element. !important is the only thing that beats !important; the alternative
   was to leave two oranges a pixel apart. */
.blog-redesign .sidebar-toc a:hover,
.blog-redesign .sidebar-toc a.active {
    color: var(--orange) !important;
}

/* The sidebar panel titles are <h2>, not <h4>, since the summary sits right
   under the article's <h1> and a jump to h4 skips two levels — an audit failure,
   and a real one for anyone navigating by headings. Their size stays what the
   panel needs: a heading level is a statement about structure, not about type
   size, and the two are only conventionally linked. */
.sidebar-toc h2,
.sidebar-share h2,
.sidebar-related h2 {
    font-size: 0.95rem;
    line-height: 1.4;
}

/* --- The closing recommendations -------------------------------------------
   The grid and the cards are product_card_v2.css, shared with the category,
   search and brand pages. Only the section around them is the blog's.

   Scoped under .article-content because the prose reaches into anything it
   contains: `.article-content img` at 0,1,1 would stretch each card's square
   image, `.article-content a` would paint every card title orange, and
   `.article-content ul li::before` would draw an orange dot in front of nothing.
   Fourth component in this file to need the same guard. */
.article-content .blog-recommended {
    margin: 48px 0 0;
    /* The ambient the card expects. It sets neither on most of its parts, so
       inside the prose it inherited 17px/1.75 and the blog's ink and drew
       differently from the same card on a category page. Measured there:
       16px/20px and #232323. */
    font-size: 16px;
    line-height: 20px;
    color: var(--searchv2-text, #232323);
}

.article-content .blog-recommended-title {
    font-size: 1.6rem;
    font-weight: 800;
    color: var(--text-dark);
    margin: 0 0 20px;
    padding-top: 0;
}

.article-content .blog-recommended img {
    width: 100%;
    margin: 0;
    border-radius: 0;
}

.article-content .blog-recommended a {
    color: inherit;
    font-weight: inherit;
}

.article-content .blog-recommended .product-card-v2-title {
    color: var(--searchv2-text, #232323);
}


/* The shared grid is written for a full-width catalogue page and lays out four
   columns; inside the 740px article column that gives 170px cards, too small to
   read a product name in. auto-fill against a 220px floor gives three here and
   one on a phone, and it stays right if the column width ever changes. */
.article-content .blog-recommended .product-card-v2-grid {
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
}

/* The sidebar product cards sat flush against each other — 0px, where the panel
   title already keeps 16px — so two bordered cards read as one box split by a
   line. The card component carries no margin of its own, deliberately: it is
   reused in a flex rail on the video page, where the gap belongs to the
   container. This panel is plain block flow, so the gap belongs here.

   `gap` on the panel rather than a margin on the card: it applies between
   siblings only, so the last card adds nothing before the panel's own padding. */
.sidebar-related {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* The panel title kept a 16px bottom margin from when this was block flow; with
   the flex gap it now adds up to 28px. 4px + the 12px gap puts it back at 16,
   which is where it was and a hair more than the cards give each other. */
.sidebar-related h2 {
    margin-bottom: 4px;
}

/* A <button> does not inherit line-height from its container the way the rest of
   the card does, so it kept `normal` where the category page gives it 20px — a
   4px shorter button for the same label. */
.article-content .blog-recommended .product-card-v2-add {
    line-height: 20px;
}

/* --- Related articles, closing the page ------------------------------------
   The cards are listing.css, shared with the index and the category grids.
   Only the section and its own grid are the article's.

   Scoped under .article-content for the same reason as the product grid: the
   prose reaches into whatever it holds — `.article-content img` would stretch a
   card's cover, `.article-content a` would paint every card title orange, and
   `.article-content h3` would repaint the card titles the way it repainted the
   product ones. */
.article-content .blog-related {
    margin: 48px 0 0;
}

.article-content .blog-related-title {
    font-size: 1.6rem;
    font-weight: 800;
    color: var(--text-dark);
    margin: 0 0 20px;
    padding-top: 0;
}

/* Three across the article column, one on a phone. auto-fill rather than a fixed
   three so a category with two other articles fills the row instead of leaving a
   hole. */
.article-content .blog-related-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
}

.article-content .blog-related img {
    width: 100%;
    margin: 0;
    border-radius: 0;
}

.article-content .blog-related a {
    color: inherit;
    font-weight: inherit;
}

.article-content .blog-related .blog-card-title {
    font-size: 1rem;
    line-height: 1.35;
    margin: 0 0 10px;
    padding-top: 0;
    color: var(--text-dark);
}

/* The figure carried no margin of its own: the image's 24px top and 12px bottom
   were the whole spacing, so the caption ended flush against the next paragraph.
   32px below separates the figure from the prose that follows it, without
   touching the 12px that ties the caption to its image.

   Not `.article-content figure` alone — the cover is a <figure> too, and its
   spacing is the hero's business. */
.article-content .article-content figure,
.article-content figure:not(.article-cover) {
    margin-bottom: 32px;
}

/* The caption inherited the prose's 1.75 leading, which on a single 13.6px line
   is 24px of line box around 14px of text — enough to push it back to 21px from
   the picture it belongs to, undoing the 12px the image's margin sets. A caption
   is one or two lines, not a paragraph. */
.article-content figcaption,
.article-content figcaption span {
    line-height: 1.4;
}

/* <picture> is an inline element, so the line box it sits in reserves room for
   descenders under the image — about 10px here, on top of the image's own 12px
   margin. That is the classic inline-image gap, and it was quietly widening the
   distance between a picture and the caption that belongs to it. Making the
   wrapper a block removes the line box altogether. */
.article-content picture {
    display: block;
    line-height: 0;
}

/* --- Section headings: a pill, then the title -------------------------------
   The mockup's .section-badge / .section-title, renamed to the blog's own
   prefix so they cannot collide with the site's section classes. Centred
   because both sections they head span the full column now that the article
   has no second track. */
.blog-section-header {
    text-align: center;
    margin-bottom: 32px;
}

.blog-section-badge {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 16px;
    padding: 6px 16px;
    border-radius: 50px;
    background: rgba(255, 107, 53, 0.1);
    border: 1px solid rgba(255, 107, 53, 0.25);
    color: var(--orange);
    font-size: 0.85rem;
    font-weight: 500;
}

/* The pill's icon rides at the text's size, like the hero's meta icons. */
.blog-section-badge .vd-icon {
    color: var(--orange);
    stroke: var(--orange);
    font-size: 0.95rem;
}

.blog-section-heading {
    margin: 0;
    font-size: clamp(1.6rem, 3vw, 2.2rem);
    font-weight: 800;
    color: var(--text-dark);
}

/* --- "Articles similaires" cards -------------------------------------------
   The mockup's .related-card. Its own card rather than the index's: that one
   badges the category over the image and offers a "Lire", which suits a
   listing being scanned; here the reader has just finished an article and what
   helps is what the next one is about. */
.blog-related-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 24px;
}

.blog-related-card {
    background: var(--white);
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.blog-related-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1);
}

.blog-related-card a {
    display: block;
    text-decoration: none;
    color: inherit;
}

/* Both blocks, for the same reason the index cards needed it: an inline box
   ignores height, and a <picture> left inline makes the image's height:100%
   resolve against nothing. */
.blog-related-card-image,
.blog-related-card-image picture {
    display: block;
    height: 180px;
}

.blog-related-card-image {
    overflow: hidden;
    background: var(--gray-light);
}

.blog-related-card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.blog-related-card-body {
    display: block;
    padding: 20px;
}

.blog-related-card-title {
    margin: 0 0 12px;
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--text-dark);
}

.blog-related-card-meta {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 0.8rem;
    color: var(--gray);
}

.blog-related-card-meta span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.blog-related-card-meta .vd-icon {
    color: var(--orange);
    stroke: var(--orange);
    font-size: 0.9rem;
}

/* --- Incomplete rows, centred -----------------------------------------------
   The shared product grid is repeat(4, 1fr) on desktop, so three products fill
   three tracks and leave a fourth empty on the right — the row reads as
   misaligned rather than as short. Scoped to the blog's closing band because
   the same grid on the category and search pages lists dozens of products,
   where a full row is the normal case and stretching is right.

   auto-fit collapses the empty tracks and the max stops the remaining ones
   from stretching to fill the row, which is what lets justify-content centre
   them. With four products nothing changes: the row is full either way. */
@media (min-width: 768px) {
    .article-closing .product-card-v2-grid,
    .article-closing .blog-related-grid {
        grid-template-columns: repeat(auto-fit, minmax(0, 280px));
        justify-content: center;
    }
}

/* --- Back to top ------------------------------------------------------------
   The article is 29 748px tall on a 360px screen and the navbar does not
   follow: by the middle of the page it sits 10 000px above the reader, and
   nothing on screen leads anywhere. This is the way back.

   z-index 900, under the mega-menu's 1000: when the menu is open it covers the
   page, and a button floating over it would be both wrong and clickable. */
.article-to-top {
    position: fixed;
    right: 16px;
    /* Clear of the iOS home indicator and of Android's gesture bar. */
    bottom: calc(16px + env(safe-area-inset-bottom));
    z-index: 900;
    display: flex;
    align-items: center;
    justify-content: center;
    /* 44px: the tap-target floor, and it is a lone control on a moving page. */
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--orange);
    color: var(--white);
    font-size: 1.4rem;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    cursor: pointer;
    /* Hidden until the reader is deep enough to want it — blog.js adds
       .is-visible past two viewports. visibility, not display: the fade needs
       something to animate, and it keeps the button out of the tab order and
       off the accessibility tree while it is not offered. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
}

.article-to-top.is-visible {
    opacity: 1;
    visibility: visible;
    transform: none;
}

.article-to-top:hover {
    background: var(--orange-light);
}

/* Above 1024px the summary sidebar is sticky and stays in reach, which is the
   thing this button stands in for. */
@media (min-width: 1025px) {
    .article-to-top {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .article-to-top {
        transition: none;
    }
}

/* The author's portrait in the byline, in place of the person glyph.

   28px: it has to read as a face at a glance while staying on the byline's own
   line — the meta row is 0.9rem text, and anything taller would set the line
   height for the date and the reading time beside it. */
.article-hero-author-photo {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    /* The source is a square avatar, but cover guarantees the circle is filled
       whatever is served the day the URL changes. */
    object-fit: cover;
    flex-shrink: 0;
}

/* --- The author card, restyled after the videos page's expert card ----------
   Same design, deliberately NOT the same component: .vd-expert-card reads its
   colours from --vd-* tokens declared on .vd in video.css, which the blog does
   not load, so reusing the classes here would render an unstyled card. And the
   videos page must keep its own two actions — this one closes an article and
   sends the reader to the channel with a single button.

   Scoped under .article-content like the rest of this card: the prose rule
   `.article-content img { width: 100% }` (0,1,1) beats a bare class and once
   rendered the 96px portrait at 690px. */
.article-content .article-author {
    align-items: center;
    background: linear-gradient(180deg, #fff 0%, var(--gray-light) 100%);
    border-left: 4px solid var(--orange);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
}

.article-content .article-author-portrait {
    position: relative;
    flex: 0 0 auto;
}

.article-content .article-author-photo {
    border: 3px solid #fff;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

.article-content .article-author-verified {
    position: absolute;
    right: -2px;
    bottom: -2px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: 3px solid #fff;
    border-radius: 50%;
    background: var(--orange);
    color: #fff;
    font-size: 18px;
}

.article-content .article-author-eyebrow {
    margin: 0;
    color: var(--gray);
    font-size: 12px;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.article-content .article-author-name {
    font-size: 1.3rem;
    margin: 2px 0;
}

.article-content .article-author-role {
    color: var(--gray);
    font-size: 14px;
    margin: 0 0 10px;
}

/* The credentials come before the quote here, so their margin flips. */
.article-content .article-author-credentials {
    margin: 0 0 12px;
}

.article-content .article-author-credentials li {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: rgba(0, 0, 0, 0.04);
    border: 0;
    color: #2b3038;
    font-size: 12.5px;
    font-weight: 700;
    padding: 5px 11px;
}

.article-content .article-author-credential-subs .vd-icon {
    color: var(--orange);
    font-size: 15px;
}

.article-content .article-author-quote {
    margin: 0;
    color: var(--text-body);
    font-size: 14.5px;
    font-style: italic;
    line-height: 1.6;
}

/* The closing sections space themselves apart.

   "Produits recommandés" and "Articles similaires" sat flush against each other
   — measured 0px between them — so the "Continuer la lecture" pill read as part
   of the product block above it rather than as the start of the next section.

   The 48px rules for these two are written as `.article-content .blog-related`
   and `.article-content .blog-recommended`, from when they lived inside the
   prose. They render in .article-closing now, so those selectors never match
   and neither margin was ever applied.

   `> * + *` rather than a margin on each: the products section only renders when
   the article recommends something, and the rule has to hold whichever of the
   two is present. */
.article-closing > * + * {
    margin-top: 48px;
}

/* Price and add-to-cart on one line, in the article's closing product grid.

   The button is taken out of the flow and pinned to the bottom-right, which
   lands it on the price's line — the price being the last thing in the text
   column above it.

   Not a flex row. The price lives INSIDE the title's <a>, so it cannot become a
   flex sibling of the button, and laying the column as a row only puts the
   button beside the whole text block: measured on a 360px screen, the text
   column is 172px and the button 96px, which left 64px for a product name. The
   alternative — display: contents on the <a> to promote its children — drops
   the link from the accessibility tree on older Safari and Firefox, and this is
   the card's main link.

   Scoped to .blog-recommended, and only there: product_card_v2.html is the
   site's shared card — the category, search and brand pages render the same
   markup and must keep their own layout. The scope wins on specificity (0,2,0
   against the component's 0,1,0), so this does not depend on load order. */
.blog-recommended {
    /* The room the button may take, and the room the price must leave it. One
       number so the two cannot disagree. 132px clears the widest measured
       state — 123px on desktop, where the label renders uppercase at 13.6px. */
    --blog-card-add-width: 132px;
}

.blog-recommended .product-card-v2-content {
    position: relative;
    /* Matches the 12px the card already keeps on its sides. At 0 the price and
       the button sat 1px off the bottom edge, which read as a cropped card. */
    padding-bottom: 12px;
    display: flex;
    flex-direction: column;
}

/* Push the price to the bottom of its own column, so it meets the button that is
   pinned to the bottom of the card.

   Without this the two only share a line when the text happens to fill the card.
   The grid stretches every card to the tallest, so a product with no review — no
   stars row — ends its text 41px higher than its neighbour, and its price floats
   well above its own button. Growing the link and pushing the price down makes
   the alignment hold whatever each card contains.

   Flex on the <a>, not `display: contents`: contents drops the link from the
   accessibility tree on older Safari and Firefox, and this is the card's main
   link. */
.blog-recommended .product-card-v2-content-link {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

/* Both offsets match the card's 12px gutter rather than sitting at 0. An
   absolutely positioned box is placed against the PADDING edge of its containing
   block, so the content's own padding does not push it in — at 0 the button sat
   flush against the card's right and bottom borders while the price kept its
   12px on the left. */
.blog-recommended .product-card-v2-actions {
    position: absolute;
    right: 12px;
    bottom: 12px;
    margin: 0;
    max-width: var(--blog-card-add-width);
}

/* The price takes the left of that line, the button the right.

   It has to be said explicitly: the card centres its text, so the price sat in
   the MIDDLE of the column while the button was pinned to the right — on a
   280px card the price ran from 749 to 831 and the button started at 806, which
   covered the last 24px and left "29,00 €" reading as "29,00". A width
   reservation does not fix that on its own: shrinking a centred box keeps it
   centred, so its right edge barely moves.

   `fit-content` rather than a full-width block: the price carries a background
   chip, which would otherwise stretch across the empty half of the line. The
   max-width is the backstop — a price long enough to reach the button wraps
   instead of sliding under it. The 12px is the gap between the two, not part of
   the button's own room. */
.blog-recommended .product-card-v2-price {
    display: block;
    width: fit-content;
    margin-top: auto;
    margin-right: auto;
    text-align: left;
    max-width: calc(100% - var(--blog-card-add-width) - 12px);
}

/* --- Prose links -----------------------------------------------------------
   There was no underline at all: colour was the only thing marking a link,
   which is WCAG 1.4.1 — someone with deuteranopia sees this orange as a grey
   barely distinct from the text. This article carries fifteen of them.

   The offset is what makes the underline usable: at 0 it cuts through the
   descenders of p, g and j, which is why it tends to get removed instead of
   fixed.

   The colour stays the brand orange by decision. Measured, #ff6b35 on white is
   2.84:1 where WCAG AA asks 4.5:1 for body text, and the prose around it sits
   at 13.3:1 — so a link reads paler than the sentence holding it. A darker
   orange (#c2410c, 5.18:1) was tried and rejected on looks; the underline and
   the weight below are what carry the link now.

   Scoped to the prose containers rather than `.article-content a`: that
   selector also reaches the product cards, the related-article cards and the
   author card, which set their own colour but not their own decoration — they
   would all have come out underlined. */
.article-content p a,
.article-content li a,
.article-content blockquote a {
    color: var(--orange);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
    border-radius: 3px;
    transition: background-color 0.2s ease, color 0.2s ease;
}

/* The hover tint is a desktop courtesy — a touch screen never sees it, which is
   exactly why the resting state above has to carry the whole signal. */
.article-content p a:hover,
.article-content li a:hover,
.article-content blockquote a:hover {
    color: var(--orange-light);
    background: rgba(255, 107, 53, 0.14);
}

.article-content p a:focus-visible,
.article-content li a:focus-visible,
.article-content blockquote a:focus-visible {
    outline: 2px solid var(--orange);
    outline-offset: 2px;
}

/* --- Image captions: a strip attached to the picture ------------------------
   The caption sat 14px below the image with no background, centred, in the
   same grey as any secondary text — nothing tied it to the picture rather than
   to the paragraph underneath, which is the one thing a caption has to say.

   So it becomes part of the figure: flush against the image, same width, on a
   tint, with the image keeping its radius on top and the caption taking it at
   the bottom. The two now read as one object.

   :has(figcaption) so a figure without a caption keeps its rounded bottom —
   where :has is unsupported the image simply keeps all four corners, which is
   the old rendering and not a broken one. */
.article-content figure:not(.article-cover):has(figcaption) img {
    margin-bottom: 0;
    border-radius: 12px 12px 0 0;
}

.article-content figure:not(.article-cover) figcaption {
    justify-content: flex-start;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    background: var(--gray-light);
    border-radius: 0 0 12px 12px;
    text-align: left;
}

/* The icon is the marker now, so it takes the accent instead of fading out. */
.article-content figure:not(.article-cover) figcaption .vd-icon {
    margin-top: 0;
    opacity: 1;
    color: var(--orange);
}

.article-content figure:not(.article-cover) figcaption span {
    text-align: left;
}

/* The sidebar's product panel is for desktop only.

   Its whole justification is that it rides ALONGSIDE the reading — see
   _partials/recommended_products.html, which notes that it lists the same
   products as the closing grid and that the sidebar is the one to drop if the
   repetition is judged too much. On a phone the sidebar is not alongside
   anything: it collapses into the flow, so the panel becomes a plain duplicate
   of the grid that closes the article.

   768px, not 1024px: at 1024 this would also hide the panel at the 1000x660
   viewport Cypress runs, and 006-add-to-cart asserts an add-to-cart FROM the
   sidebar. Hiding it there would turn a design decision into a broken test. */
@media (max-width: 768px) {
    .article-sidebar .sidebar-related {
        display: none;
    }
}

/* --- Mobile products bar ----------------------------------------------------
   A sticky bar naming the article's products, opening them as a bottom sheet.

   It exists because the sidebar's product panel is hidden below 768px: a
   sidebar that is no longer beside the text is a duplicate of the closing grid,
   but removing it left a phone reader with no route to the products until the
   end of a 40-viewport article.

   Desktop never shows it — the sidebar panel is right there, sticky. */
.blog-products-bar {
    display: none;
}

@media (max-width: 768px) {
    .blog-products-bar {
        display: block;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* Under the mega-menu (1000) and under the modal it opens, above the
           page and above the back-to-top button. */
        z-index: 850;
        padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
        background: rgba(255, 255, 255, 0.96);
        backdrop-filter: blur(8px);
        border-top: 1px solid rgba(0, 0, 0, 0.08);
    }

    .blog-products-bar-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        width: 100%;
        /* 48px: a full-width primary action, comfortably above the 44px floor. */
        min-height: 48px;
        padding: 0 16px;
        border: 0;
        border-radius: 50px;
        background: var(--orange);
        color: var(--white);
        font: inherit;
        font-size: 15px;
        font-weight: 700;
        cursor: pointer;
    }

    .blog-products-bar-btn .vd-icon {
        flex-shrink: 0;
        font-size: 18px;
    }

    /* The back-to-top button clears the bar instead of sitting on it. :has so an
       article that recommends nothing — no bar rendered — keeps the button where
       it was; where :has is unsupported it simply stays there too. */
    body:has(.blog-products-bar) .article-to-top {
        bottom: calc(84px + env(safe-area-inset-bottom));
    }
}

/* The sheet's list: the same mini cards the sidebar uses, stacked. */
.blog-products-sheet-list {
    display: grid;
    gap: 12px;
    padding-bottom: 8px;
}

/* --- The byline: portrait left, two lines right -----------------------------
   It used to be four chips in a centred wrapping row: at 360px that came out
   71px tall over two rows, with the reading time and the update date alone on
   the second one, reading as leftovers rather than as a line.

   The block stays centred in the hero — the title and the standfirst above it
   are — but its own text is left-aligned, because two ragged centred lines
   beside a round portrait have no axis to sit on. */
.article-hero-meta {
    display: inline-flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 12px;
    text-align: left;
}

.article-hero-meta .article-hero-author-photo {
    width: 40px;
    height: 40px;
}

.article-hero-meta-text {
    min-width: 0;
}

.article-hero-byline {
    margin: 0 0 2px;
    color: var(--gray);
    font-size: 0.9rem;
    line-height: 1.35;
}

/* The dates and the reading time: facts about the article, one notch quieter
   than the line naming who wrote it. */
.article-hero-dates {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px 8px;
    margin: 0;
    color: var(--gray);
    font-size: 0.82rem;
    line-height: 1.35;
}

.article-hero-dates span {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

/* No middot separator: each fact carries its own icon, which already separates
   them, and a dot on top would only add noise. */

/* The byline's own row, under the cover.

   It used to sit inside the hero, above the picture. The media is what tells a
   reader what an article is about, and at 360px it started at 666px — a 74px
   sliver at the bottom of the first screen, which was also all the browser
   counted as the LCP element. Order is now breadcrumb, title, media, byline.

   Same column as the cover (900px, 20px gutters) so the two align. */
.article-byline-row {
    max-width: 900px;
    margin: 0 auto;
    padding: 16px 20px 0;
}

/* The standfirst's own row, between the media and the byline.

   Same column as the cover and the byline (900px, 20px gutters), and still
   centred: it sits on the axis the title above it is set on. */
.article-standfirst-row {
    max-width: 900px;
    margin: 0 auto;
    padding: 16px 20px 0;
    text-align: center;
}

.article-standfirst-row .article-hero-subtitle {
    margin-bottom: 0;
}
