{% extends 'application/whileresume/website/layout-social.html.twig' %}{% set paramArticle = getCoreToolsList("article") %}{% block title %}{{ article.title }}{% endblock title %}{% block description %}{{ article.shortDescription }}{% endblock description %}{% block robots %}index,follow{% endblock robots %}{% block meta_social %}{{ parent() }}{% include "/vitrine/components/socialmedia_articles.html.twig" with { 'social_type':'article','article':article } %}{% endblock meta_social %}{% block meta %}{{ parent() }}{% if article.author is not null and article.author is not empty %}<meta name="author" content="{{ article.author }}" />{% endif %}{% endblock meta %}{% block canonical %}{% include "/vitrine/lexend/articles/components/canonical.html.twig" with {'article':article} %}{% endblock canonical %}{% block css %} {{ parent() }} <style> /* ═══════════════════════════════════════════════════════════════ PAGE ARTICLE — pattern jobs/show (1 colonne max-width 880px) ═══════════════════════════════════════════════════════════════ */ .article-layout{display:block;max-width:880px;margin:0 auto} /* ─── Header card (titre + meta) ─── */ .article-card-main{position:relative;background:#fff;border-radius:16px;box-shadow:0 4px 20px 0 rgba(0,0,0,0.06);overflow:hidden;margin-bottom:14px} .article-card-cover{position:relative;width:100%;height:180px;background:linear-gradient(135deg,#6C3AED 0%,#8B5CF6 50%,#A78BFA 100%);overflow:hidden} .article-card-cover img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover} .article-card-cover-overlay{position:absolute;inset:0;background:linear-gradient(to bottom,rgba(0,0,0,.1) 0%,rgba(0,0,0,.55) 100%)} @media(max-width:480px){.article-card-cover{height:140px}} .article-card-head{padding:20px 24px 18px} @media(max-width:480px){.article-card-head{padding:16px 18px 14px}} .article-card-title{font-size:24px;font-weight:800;color:#1E1B2E;line-height:1.25;letter-spacing:-0.02em;margin:0 0 10px} @media(min-width:768px){.article-card-title{font-size:28px}} @media(max-width:480px){.article-card-title{font-size:22px}} .article-card-subtitle{font-size:15px;color:#4B5563;line-height:1.5;margin:0 0 14px} @media(max-width:480px){.article-card-subtitle{font-size:14px}} .article-card-meta{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:0} .article-card-meta-item{display:inline-flex;align-items:center;gap:5px;padding:5px 11px;border-radius:100px;background:#F3F4F6;font-size:12px;color:#4B5563;font-weight:500} .article-card-meta-item svg{width:12px;height:12px;flex-shrink:0;color:#6B7280} .article-card-meta-featured{background:#FEF3C7;color:#92400E;font-weight:600} .article-card-meta-featured svg{color:currentColor} /* ─── TOC sommaire flottant (bas à gauche, minimaliste) ─── */ .article-toc-fab{ position:fixed; left:20px; bottom:20px; z-index:100; } @media(max-width:991px){ .article-toc-fab{left:16px;bottom:16px} } /* Bouton FAB (état fermé) */ .article-toc-trigger{ display:inline-flex;align-items:center;gap:8px; background:#1E1B2E; color:#fff; border:none; border-radius:100px; padding:11px 18px 11px 14px; font-size:13px;font-weight:600; font-family:inherit; cursor:pointer; box-shadow:0 8px 24px rgba(30,27,46,.28),0 2px 6px rgba(30,27,46,.18); transition:transform .2s ease,box-shadow .2s ease,background .15s ease; } .article-toc-trigger:hover{ transform:translateY(-2px); box-shadow:0 12px 30px rgba(30,27,46,.35),0 3px 8px rgba(30,27,46,.22); background:var(--theme-color,#6C3AED); } .article-toc-trigger-icon{ display:inline-flex;align-items:center;justify-content:center; width:24px;height:24px;border-radius:50%; background:rgba(255,255,255,.15); flex-shrink:0; } .article-toc-trigger-icon svg{width:12px;height:12px} .article-toc-trigger-count{ display:inline-flex;align-items:center;justify-content:center; min-width:20px;height:20px;padding:0 6px;border-radius:100px; background:var(--theme-color,#6C3AED); color:#fff; font-size:10.5px;font-weight:700; line-height:1; margin-left:2px; } .article-toc-trigger.has-active .article-toc-trigger-count{ background:#fff; color:var(--theme-color,#6C3AED); } @media(max-width:540px){ .article-toc-trigger-label{display:none} .article-toc-trigger{padding:11px 12px} } /* Panneau (état ouvert) */ .article-toc-panel{ position:absolute; left:0; bottom:calc(100% + 10px); width:320px; max-width:calc(100vw - 40px); background:#fff; border-radius:16px; box-shadow:0 16px 48px rgba(0,0,0,.18),0 4px 12px rgba(0,0,0,.08); opacity:0; transform:translateY(8px) scale(.96); transform-origin:bottom left; pointer-events:none; transition:opacity .2s ease,transform .2s ease; overflow:hidden; } .article-toc-fab.is-open .article-toc-panel{ opacity:1; transform:translateY(0) scale(1); pointer-events:auto; } @media(max-width:540px){ .article-toc-panel{width:calc(100vw - 32px);max-width:340px} } .article-toc-panel-header{ display:flex;align-items:center;gap:10px; padding:14px 16px; border-bottom:1px solid #F3F4F6; } .article-toc-panel-icon{ display:inline-flex;align-items:center;justify-content:center; width:28px;height:28px;border-radius:8px; background:#F5F3FF; color:var(--theme-color,#6C3AED); flex-shrink:0; } .article-toc-panel-icon svg{width:13px;height:13px} .article-toc-panel-label{ font-size:11px;font-weight:700; color:#1E1B2E; text-transform:uppercase;letter-spacing:.08em; flex:1;margin:0; } .article-toc-panel-close{ background:transparent;border:0; color:#9CA3AF;cursor:pointer; padding:4px;line-height:1; display:inline-flex; border-radius:6px; transition:background .15s,color .15s; } .article-toc-panel-close:hover{background:#F3F4F6;color:#1E1B2E} .article-toc-panel-close svg{width:14px;height:14px} .article-toc-panel-body{ padding:14px 16px; max-height:60vh; overflow-y:auto; } .article-toc-panel-body::-webkit-scrollbar{width:4px} .article-toc-panel-body::-webkit-scrollbar-thumb{background:#E5E7EB;border-radius:4px} .article-summary ul,.article-summary ol{padding-left:0;margin:0;list-style:none} .article-summary li{font-size:13px;line-height:1.5;margin-bottom:6px;font-weight:500;position:relative;padding-left:14px} .article-summary li::before{content:"";position:absolute;left:0;top:8px;width:5px;height:5px;border-radius:50%;background:#D1D5DB;transition:background .15s ease,transform .15s ease} .article-summary li:hover::before{background:var(--theme-color,#6C3AED);transform:scale(1.4)} .article-summary li a{color:#4B5563;text-decoration:none;transition:color .15s ease;display:block} .article-summary li a:hover{color:var(--theme-color,#6C3AED)} .article-summary li.active>a{color:var(--theme-color,#6C3AED);font-weight:700} .article-summary li.active::before{background:var(--theme-color,#6C3AED);transform:scale(1.4)} .article-summary ul ul,.article-summary ol ol{margin-top:6px;padding-left:14px} /* Backdrop discret (clic en dehors pour fermer) */ .article-toc-backdrop{ position:fixed;inset:0; background:transparent; z-index:99; opacity:0; pointer-events:none; transition:opacity .2s ease; } .article-toc-backdrop.is-visible{ opacity:1; pointer-events:auto; } /* ─── Contenu de l'article ─── */ .article-card-content{background:#fff;border-radius:16px;box-shadow:0 4px 20px 0 rgba(0,0,0,0.06);overflow:hidden;margin-bottom:14px} .article-card-section{padding:24px} @media(max-width:480px){.article-card-section{padding:18px}} .article-content{font-size:15px;line-height:1.75;color:#374151} .article-content h2{font-size:22px;font-weight:700;color:#1E1B2E;margin:28px 0 12px;line-height:1.3;letter-spacing:-0.01em;scroll-margin-top:80px} .article-content h2:first-child{margin-top:0} .article-content h3{font-size:18px;font-weight:700;color:#1E1B2E;margin:22px 0 10px;line-height:1.3;scroll-margin-top:80px} .article-content h4{font-size:16px;font-weight:700;color:#1E1B2E;margin:18px 0 8px} .article-content p{margin-bottom:14px} .article-content a{color:var(--theme-color,#6C3AED);text-decoration:none;font-weight:500} .article-content a:hover{text-decoration:underline} .article-content ul,.article-content ol{padding-left:24px;margin-bottom:14px} .article-content li{margin-bottom:6px} .article-content img{max-width:100%;height:auto;border-radius:12px;margin:16px 0} .article-content blockquote{border-left:4px solid var(--theme-color,#6C3AED);padding:10px 18px;margin:16px 0;background:#F5F3FF;border-radius:0 8px 8px 0;font-style:italic;color:#4B5563} .article-content code{background:#F3F4F6;padding:2px 6px;border-radius:4px;font-size:13px;color:#D6336C;font-family:"SF Mono",Menlo,Monaco,Consolas,monospace} .article-content pre{background:#1E1B2E;color:#F3F4F6;padding:18px;border-radius:10px;overflow-x:auto;margin:16px 0;font-size:13px;line-height:1.6} .article-content pre code{background:transparent;color:inherit;padding:0} .article-content table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14px} .article-content th,.article-content td{padding:10px 12px;border:1px solid #E5E7EB;text-align:left} .article-content th{background:#F9FAFB;font-weight:700;color:#1E1B2E} /* ─── Tags en bas du contenu ─── */ .article-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:24px;padding-top:18px;border-top:1px solid #F3F4F6} .article-tag{display:inline-flex;align-items:center;padding:5px 11px;border-radius:100px;background:#F5F3FF;color:var(--theme-color,#6C3AED);font-size:12px;font-weight:600;text-decoration:none;transition:background .15s,transform .15s} .article-tag:hover{background:#EDE9FE;transform:translateY(-1px);color:var(--theme-color,#6C3AED)} .article-tag::before{content:"#";opacity:.6;margin-right:1px} /* ─── CTA inline (Recruteur / Candidat) ─── */ .article-cta{position:relative;border-radius:18px;padding:24px;margin-bottom:14px;background:linear-gradient(135deg,#6C3AED 0%,#8B5CF6 50%,#A78BFA 100%);overflow:hidden;box-shadow:0 10px 30px -8px rgba(108,58,237,.35)} .article-cta::before{content:"";position:absolute;top:-40px;right:-40px;width:180px;height:180px;background:radial-gradient(circle,rgba(255,255,255,.15) 0%,transparent 70%);pointer-events:none} .article-cta::after{content:"";position:absolute;bottom:-60px;left:-60px;width:200px;height:200px;background:radial-gradient(circle,rgba(255,255,255,.08) 0%,transparent 70%);pointer-events:none} .article-cta-eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700;color:#fff;text-transform:uppercase;letter-spacing:.1em;background:rgba(255,255,255,.18);padding:5px 12px;border-radius:100px;margin-bottom:12px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);position:relative;z-index:2} .article-cta-eyebrow svg{width:12px;height:12px} .article-cta-title{font-size:20px;font-weight:800;color:#fff;line-height:1.25;letter-spacing:-0.01em;margin:0 0 18px;position:relative;z-index:2} @media(min-width:768px){.article-cta-title{font-size:24px}} .article-cta-grid{display:grid;grid-template-columns:1fr;gap:12px;position:relative;z-index:2} @media(min-width:640px){.article-cta-grid{grid-template-columns:1fr 1fr;gap:14px}} .article-cta-side{background:rgba(255,255,255,.97);border-radius:14px;padding:18px;display:flex;flex-direction:column;transition:transform .2s,box-shadow .2s} .article-cta-side:hover{transform:translateY(-2px);box-shadow:0 12px 24px -8px rgba(0,0,0,.18)} .article-cta-side-head{display:flex;align-items:center;gap:10px;margin-bottom:10px} .article-cta-side-icon{width:38px;height:38px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#EDE9FE,#DDD6FE);color:var(--theme-color,#6C3AED);flex-shrink:0} .article-cta-side-icon svg{width:18px;height:18px} .article-cta-side-label{font-size:11px;font-weight:700;color:var(--theme-color,#6C3AED);text-transform:uppercase;letter-spacing:.08em;margin:0} .article-cta-side-heading{font-size:15px;font-weight:700;color:#1E1B2E;line-height:1.3;margin:0 0 8px} .article-cta-side-text{font-size:13px;color:#6B7280;line-height:1.5;margin:0 0 14px;flex:1} .article-cta-side-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;background:var(--theme-color,#6C3AED);color:#fff;text-decoration:none;padding:11px 16px;border-radius:10px;font-size:13px;font-weight:700;letter-spacing:.01em;transition:background .15s,transform .15s} .article-cta-side-btn:hover{background:#5B21B6;color:#fff;transform:translateX(2px)} .article-cta-side-btn svg{width:14px;height:14px;transition:transform .15s} .article-cta-side-btn:hover svg{transform:translateX(2px)} /* ─── Articles similaires (style identique aux jobs) ─── */ .similar-section-title{font-size:11px;font-weight:700;color:#9CA3AF;text-transform:uppercase;letter-spacing:.08em;margin:32px 0 12px;padding-left:4px;display:inline-flex;align-items:center;gap:8px} .similar-card{background:#fff;border-radius:14px;padding:14px;box-shadow:0 0 16px 0 rgba(0,0,0,0.04);margin-bottom:10px;display:flex;align-items:center;gap:14px;text-decoration:none;color:inherit;transition:transform .15s,box-shadow .2s} .similar-card:hover{transform:translateY(-1px);box-shadow:0 4px 20px rgba(108,58,237,.1);color:inherit} .similar-card-logo{width:50px;height:50px;border-radius:12px;background:linear-gradient(135deg,#EDE9FE,#DDD6FE);display:flex;align-items:center;justify-content:center;color:var(--theme-color,#6C3AED);flex-shrink:0;overflow:hidden} .similar-card-logo img{width:100%;height:100%;object-fit:cover;border-radius:8px} .similar-card-logo svg{width:24px;height:24px;opacity:.9} .similar-card-info{flex:1;min-width:0} .similar-card-title{font-size:14px;font-weight:700;color:#1E1B2E;line-height:1.3;margin:0 0 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .similar-card-meta{font-size:12px;color:#6B7280;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:8px} .similar-card-meta-sep{display:inline-block;width:3px;height:3px;border-radius:50%;background:#D1D5DB;flex-shrink:0} .similar-card-arrow{flex-shrink:0;color:#9CA3AF;transition:color .15s,transform .15s} .similar-card:hover .similar-card-arrow{color:var(--theme-color,#6C3AED);transform:translateX(2px)} .similar-card-arrow svg{width:18px;height:18px} /* ─── Section "Offres recommandées" (cross-sell article -> jobs) ─── */ .recommended-jobs-header{ display:flex;align-items:center;justify-content:space-between;gap:10px; margin:32px 0 12px;padding-left:4px;flex-wrap:wrap; } .recommended-jobs-header .similar-section-title{margin:0} .recommended-jobs-icon{ display:inline-flex;align-items:center;justify-content:center; width:22px;height:22px;border-radius:6px; background:#F5F3FF; color:var(--theme-color,#6C3AED); flex-shrink:0; } .recommended-jobs-icon svg{width:11px;height:11px} .recommended-jobs-all{ display:inline-flex;align-items:center;gap:4px; font-size:12px;font-weight:600; color:var(--theme-color,#6C3AED); text-decoration:none; padding:5px 10px;border-radius:100px; border:1px dashed rgba(108,58,237,.35); transition:background .15s,border-style .15s,transform .15s; } .recommended-jobs-all:hover{ background:#F5F3FF; border-style:solid; color:var(--theme-color,#6C3AED); transform:translateX(2px); } .recommended-jobs-all svg{width:11px;height:11px} /* Grille des cards jobs (style identique au composant _jobs_content.html.twig) */ .recommended-jobs-grid{margin-bottom:14px} .recommended-jobs-grid .job-card{ display:flex;flex-direction:column;gap:.75rem; height:100%; padding:1.25rem; background:#fff; border:1px solid #e6e8ec; border-radius:14px; text-decoration:none;color:inherit; transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease; } .recommended-jobs-grid .job-card:hover{ transform:translateY(-2px); box-shadow:0 8px 24px rgba(20,24,40,.08); border-color:#d6d9e0; text-decoration:none;color:inherit; } .recommended-jobs-grid .job-card__header{display:flex;align-items:center;gap:.75rem} .recommended-jobs-grid .job-card__logo{ width:44px;height:44px; border-radius:10px; object-fit:cover; background:#f4f5f8; flex-shrink:0; } .recommended-jobs-grid .job-card__logo--placeholder{ display:flex;align-items:center;justify-content:center; font-weight:700;color:#6b7280; background:linear-gradient(135deg,#eef2ff,#f5f3ff); } .recommended-jobs-grid .job-card__company{ display:flex;flex-direction:column; min-width:0; } .recommended-jobs-grid .job-card__company-name{ font-weight:600;font-size:.9rem;color:#111827; white-space:nowrap;overflow:hidden;text-overflow:ellipsis; } .recommended-jobs-grid .job-card__location{ font-size:.8rem;color:#6b7280; display:inline-flex;align-items:center;gap:.25rem; } .recommended-jobs-grid .job-card__summary{ font-size:.875rem;color:#4b5563; margin:0;line-height:1.45; display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical; overflow:hidden; } .recommended-jobs-grid .job-card__footer{ display:flex;flex-wrap:wrap;gap:.375rem; margin-top:auto;padding-top:.5rem; } .recommended-jobs-grid .job-card__tag{ font-size:.72rem;font-weight:500; padding:.25rem .55rem; border-radius:999px; background:#f3f4f6;color:#374151; } .recommended-jobs-grid .job-card__tag--accent{ background:#eef2ff;color:#4338ca; } /* Bouton retour vers la liste */ .article-back-link{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;background:#fff;border:1px solid #E5E7EB;border-radius:10px;font-size:12px;font-weight:600;color:#4B5563;text-decoration:none;margin-bottom:14px;transition:border-color .15s,color .15s} .article-back-link:hover{border-color:var(--theme-color,#6C3AED);color:var(--theme-color,#6C3AED)} .article-back-link svg{width:13px;height:13px} </style>{% endblock css %}{% block body %} <div class="article-layout"> {# ═══ Card principale : titre + meta + cover ═══ #} <article class="article-card-main"> {# Cover (si image) #} {% if article.image.name is not null %} <div class="article-card-cover"> <img src="{{ vich_uploader_asset(article, 'imageFile') }}" alt="{{ article.title }}" /> <div class="article-card-cover-overlay"></div> </div> {% endif %} <div class="article-card-head"> <h1 class="article-card-title">{{ article.title }}</h1> {% if article.subtitle is not empty %} <p class="article-card-subtitle">{{ article.subtitle }}</p> {% endif %} <div class="article-card-meta"> {% if article.author is not null and article.author is not empty %} <span class="article-card-meta-item"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/> </svg> {{ article.author }} </span> {% endif %} {#if article.pageslug is not empty %} <span class="article-card-meta-item"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/> </svg> {{ article.pageslug }} </span> {% endif %#} </div> </div> </article> {# ═══ Sommaire flottant en bas à gauche (FAB) ═══ #} {% set summaryHtml = autosummary(article.description) %} {% if summaryHtml is not empty %} <div class="article-toc-fab" id="articleTocFab"> {# Panneau (au-dessus du bouton, masqué par défaut) #} <div class="article-toc-panel" id="articleTocPanel" role="dialog" aria-label="{{ 'article.summary.title'|trans({}, 'vitrine-lexend') }}"> <div class="article-toc-panel-header"> <span class="article-toc-panel-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/> </svg> </span> <span class="article-toc-panel-label">{{ 'article.summary.title'|trans({}, 'vitrine-lexend') }}</span> <button type="button" class="article-toc-panel-close" id="articleTocClose" aria-label="Close"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/> </svg> </button> </div> <div class="article-toc-panel-body"> <div class="summary article-summary">{{ summaryHtml|raw }}</div> {% if is_granted('ROLE_SUPER_ADMIN') %} <div style="margin-top:12px;padding-top:10px;border-top:1px solid #F3F4F6;"> <a href="{{ path('bo_articles_edit',{'id':article.id}) }}" style="font-size:11px;font-weight:600;color:#DC2626;text-transform:uppercase;letter-spacing:.06em;text-decoration:none;"> <i class="feather-edit-2 me-1"></i>{{ 'article.summary.edit'|trans({}, 'vitrine-lexend') }} </a> </div> {% endif %} </div> </div> {# Bouton FAB (toujours visible) #} <button type="button" class="article-toc-trigger" id="articleTocTrigger" aria-expanded="false"> <span class="article-toc-trigger-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/> </svg> </span> <span class="article-toc-trigger-label">{{ 'article.summary.title'|trans({}, 'vitrine-lexend') }}</span> <span class="article-toc-trigger-count" id="articleTocCount">·</span> </button> </div> <div class="article-toc-backdrop" id="articleTocBackdrop"></div> {% endif %} {# ═══ Contenu de l'article ═══ #} <div class="article-card-content"> <div class="article-card-section"> <div id="single-post" class="post-content article-content"> {{ contentArticle(autosummaryID(article.description))|raw }} </div> {# Tags #} {% if article.tags is not empty %} <div class="article-tags"> {% for tag in article.tags|split(',') %} {% set t = tag|trim %} {% if t is not empty %} <span class="article-tag">{{ t }}</span> {% endif %} {% endfor %} </div> {% endif %} </div> </div> {# ═══ CTA Recruteur / Candidat ═══ #} {% if getEnv("KERNEL_APPLICATION") == "whileresume" and app.request.locale in ['fr','en'] %} <div class="article-cta"> <span class="article-cta-eyebrow"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/> </svg> Whileresume </span> <h2 class="article-cta-title"> {% if app.request.locale == 'fr' %} Talents et entreprises se rencontrent ici. {% else %} Where talent meets fast-growing companies. {% endif %} </h2> <div class="article-cta-grid"> <div class="article-cta-side"> <div class="article-cta-side-head"> <span class="article-cta-side-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <rect x="2" y="7" width="20" height="14" rx="2" ry="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/> </svg> </span> <span class="article-cta-side-label">{{ app.request.locale == 'fr' ? 'Recruteur' : 'Recruiter' }}</span> </div> <h3 class="article-cta-side-heading"> {{ app.request.locale == 'fr' ? "Recrutez des profils d'exception, plus vite." : 'Hire exceptional talent, faster.' }} </h3> <p class="article-cta-side-text"> {% if app.request.locale == 'fr' %} Accédez aux meilleurs talents du marché et connectez-vous directement à des candidats qualifiés en quête de leur prochain défi. {% else %} Get access to top market talent and connect directly with qualified candidates ready for their next challenge. {% endif %} </p> <a href="{{ path('whileresume_business_' ~ app.request.locale) }}" class="article-cta-side-btn"> {{ app.request.locale == 'fr' ? 'Je recrute' : "I'm recruiting" }} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/> </svg> </a> </div> <div class="article-cta-side"> <div class="article-cta-side-head"> <span class="article-cta-side-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/> </svg> </span> <span class="article-cta-side-label">{{ app.request.locale == 'fr' ? 'Candidat' : 'Candidate' }}</span> </div> <h3 class="article-cta-side-heading"> {{ app.request.locale == 'fr' ? 'Trouvez le job qui vous ressemble.' : 'Find the job that fits you.' }} </h3> <p class="article-cta-side-text"> {% if app.request.locale == 'fr' %} On vous accompagne dans la recherche de votre poste idéal au sein des entreprises les plus prometteuses du marché. {% else %} We support you in finding your ideal position within the most promising companies on the market. {% endif %} </p> <a href="{{ path('whileresume_resume_' ~ app.request.locale) }}" class="article-cta-side-btn"> {{ app.request.locale == 'fr' ? 'Je cherche un job' : "I'm looking for a job" }} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/> </svg> </a> </div> </div> </div> {% endif %} {% if article.ctaHTML is not empty %}{{ article.ctaHTML|raw }}{% endif %} {# ═══ Articles similaires ═══ #} {% if similarArticles is defined and similarArticles is not empty %} <h4 class="similar-section-title"> {{ app.request.locale == 'fr' ? 'Articles similaires' : 'Similar articles' }} · {{ similarArticles|length }} </h4> {% for sa in similarArticles %} {# Construction de l'URL article (idem que dans list.html.twig) #} {% set prefix = "" %} {% set urlSa = path('cvs_website_article',{'slug': sa.slug}) %} {% if app.request.locale != default_locale %} {% set urlSa = path('locale_cvs_website_article',{'_locale':app.request.locale,'slug': sa.slug}) %} {% set prefix = "/" ~ app.request.locale %} {% endif %} {% if sa.pageslug3 is not empty %} {% set urlSa = prefix ~ '/' ~ sa.pageslug ~ '/' ~ sa.pageslug2 ~ '/' ~ sa.pageslug3 %} {% elseif sa.pageslug2 is not empty %} {% set urlSa = prefix ~ '/' ~ sa.pageslug ~ '/' ~ sa.pageslug2 %} {% elseif sa.pageslug is not empty %} {% set urlSa = prefix ~ '/' ~ sa.pageslug %} {% endif %} <a href="{{ urlSa }}" class="similar-card"> <div class="similar-card-logo"> {% if sa.image.name is not null %} <img src="{{ vich_uploader_asset(sa, 'imageFile') }}" alt=""> {% else %} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/> <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/> </svg> {% endif %} </div> <div class="similar-card-info"> <div class="similar-card-title">{{ sa.title }}</div> <div class="similar-card-meta"> <span>{{ sa.publishedAt|date("d M Y") }}</span> {% if sa.author is not null and sa.author is not empty %} <span class="similar-card-meta-sep"></span> <span>{{ sa.author }}</span> {% endif %} {% if sa.pageslug is not empty %} <span class="similar-card-meta-sep"></span> <span>{{ sa.pageslug }}</span> {% endif %} </div> </div> <span class="similar-card-arrow"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="9 18 15 12 9 6"/> </svg> </span> </a> {% endfor %} {% endif %} {# ═══ Jobs aléatoires (style identique au composant _jobs_content) ═══ #} {% if recommendedJobs is defined and recommendedJobs is not empty %} <div class="recommended-jobs-header"> <h4 class="similar-section-title" style="margin-bottom:0"> <span class="recommended-jobs-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <rect x="2" y="7" width="20" height="14" rx="2" ry="2"/> <path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/> </svg> </span> {{ app.request.locale == 'fr' ? 'Découvrez nos offres' : 'Discover our jobs' }} · {{ recommendedJobs|length }} </h4> <a href="{% if app.request.locale == 'en' %}{{ path('whileresume_jobs_list') }}{% else %}{{ path('locale_whileresume_jobs_list',{'_locale':app.request.locale}) }}{% endif %}" class="recommended-jobs-all"> {{ app.request.locale == 'fr' ? 'Voir toutes les offres' : 'See all jobs' }} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <polyline points="9 18 15 12 9 6"/> </svg> </a> </div> <div class="row g-3 recommended-jobs-grid"> {% for k in recommendedJobs %} <div class="col-md-6 col-lg-4"> <a href="{% if k.locale == 'fr' %}{{ path('locale_cvs_application_job_show',{'_locale':app.request.locale,'slug':k.slug}) }}{% else %}{{ path('cvs_application_job_show',{'slug':k.slug}) }}{% endif %}" class="job-card"> <div class="job-card__header"> {% if k.image and k.image.name %} <img src="{{ vich_uploader_asset(k, 'imageFile') }}" alt="{{ k.companyName }}" class="job-card__logo"> {% else %} <div class="job-card__logo job-card__logo--placeholder"> {{ k.companyName|default('?')|slice(0, 1)|upper }} </div> {% endif %} <div class="job-card__company"> <span class="job-card__company-name">{{ k.jobTitle }}</span> <span class="job-card__location"> <i class="fas fa-map-marker-alt"></i> {{ k.city }}{% if k.country %}, {{ k.country }}{% endif %} </span> </div> </div> {% set summary = k.jobSummary ?: k.shortDescription %} {% if summary %} <p class="job-card__summary">{{ summary|striptags|slice(0, 140) }}{% if summary|length > 140 %}…{% endif %}</p> {% endif %} <div class="job-card__footer"> {% if k.employmentType %}<span class="job-card__tag">{{ k.employmentType }}</span>{% endif %} {% if k.remoteWork %}<span class="job-card__tag job-card__tag--accent">{{ k.remoteWork }}</span>{% endif %} </div> </a> </div> {% endfor %} </div> {% endif %} </div>{% endblock body %}{% block footer_js %} {{ parent() }} <script> (function() { var fab = document.getElementById('articleTocFab'); var trigger = document.getElementById('articleTocTrigger'); var panel = document.getElementById('articleTocPanel'); var closeBtn = document.getElementById('articleTocClose'); var backdrop = document.getElementById('articleTocBackdrop'); var countEl = document.getElementById('articleTocCount'); if (!fab || !trigger) return; // ── Ouvrir / fermer le panneau ── function openPanel(){ fab.classList.add('is-open'); if (backdrop) backdrop.classList.add('is-visible'); trigger.setAttribute('aria-expanded', 'true'); } function closePanel(){ fab.classList.remove('is-open'); if (backdrop) backdrop.classList.remove('is-visible'); trigger.setAttribute('aria-expanded', 'false'); } function togglePanel(){ if (fab.classList.contains('is-open')) closePanel(); else openPanel(); } trigger.addEventListener('click', function(e){ e.stopPropagation(); togglePanel(); }); if (closeBtn) closeBtn.addEventListener('click', closePanel); if (backdrop) backdrop.addEventListener('click', closePanel); // Échap pour fermer document.addEventListener('keydown', function(e){ if (e.key === 'Escape' && fab.classList.contains('is-open')) closePanel(); }); // Clic en dehors (au cas où, pour les contextes sans backdrop) document.addEventListener('click', function(e){ if (!fab.classList.contains('is-open')) return; if (panel && !panel.contains(e.target) && !trigger.contains(e.target)) closePanel(); }); // ── Scroll spy + compteur ── var summaryLinks = document.querySelectorAll('.article-summary a[href^="#"]'); if (countEl) { countEl.textContent = summaryLinks.length || '·'; } if (!summaryLinks.length) return; var targets = []; summaryLinks.forEach(function(link) { var id = link.getAttribute('href').replace('#', ''); var target = document.getElementById(id); if (target) targets.push({ link: link, target: target }); }); if (!targets.length) return; function onScroll() { var scrollY = window.scrollY + 120; var current = null; var currentIndex = 0; targets.forEach(function(item, i) { if (item.target.offsetTop <= scrollY) { current = item; currentIndex = i; } }); summaryLinks.forEach(function(l) { l.parentElement.classList.remove('active'); }); if (current) { current.link.parentElement.classList.add('active'); if (countEl) { countEl.textContent = (currentIndex + 1) + '/' + targets.length; trigger.classList.add('has-active'); } } else { if (countEl) countEl.textContent = targets.length; trigger.classList.remove('has-active'); } } window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); // Ferme le panneau quand on clique sur un lien d'ancre summaryLinks.forEach(function(link){ link.addEventListener('click', function(){ setTimeout(closePanel, 150); }); }); })(); </script>{% endblock footer_js %}