/* The calendar pages: /calendrier and /calendrier-geneve (programme-calendrier.js).
   Moved out of content/calendrier.md's own <style> so both share it. Linked from
   the page after the FullCalendar and tippy scripts, not bundled in <head>: those
   inject their own CSS, and ours has to come after it to win at equal specificity. */
#calendar {
/* Le calendrier prend les jetons du site plutôt que ceux de bootstrap. */
font-family: Poppins, system-ui, sans-serif;
--fc-border-color: var(--line);
/* Le jour courant n'est plus teinté : le cadre orange le dit (voir today-flash
   plus bas), et une case blanche comme les autres est ce à quoi le clignotement
   revient. */
--fc-today-bg-color: transparent;
--fc-page-bg-color: var(--bg);
--fc-button-bg-color: var(--orange);
--fc-button-border-color: var(--orange);
--fc-button-hover-bg-color: var(--orange-dark);
--fc-button-hover-border-color: var(--orange-dark);
--fc-button-active-bg-color: var(--orange-dark);
--fc-button-active-border-color: var(--orange-dark);
--fc-neutral-bg-color: var(--ice);
}
/* Le calendrier est une carte, comme celles de l'agenda : la grille du mois
garde son propre cadre, la carte lui donne le fond, le rayon et l'ombre. */
/* Le calendrier déborde de la colonne de texte : --cal-max est sa largeur
maximale, bornée à ce que l'écran permet, et la marge négative le recentre —
la sortie de colonne de .img-full et des bandes de couleur. --cal-max: 100%
redonnerait calc(50% - 50%) = 0, soit exactement la largeur de la colonne de
texte. Posée sur le conteneur et non sur #calendar : la légende est son autre
enfant (programme-calendrier.js l'insère là), et elle doit sortir avec lui,
sinon la barre de filtres reste étroite au-dessus d'un calendrier large. */
.sqs-code-container:has(#calendar) {
--cal-max: 100rem;
--cal-w: min(var(--cal-max), calc(100vw - 2 * var(--gutter)));
width: var(--cal-w);
margin-left: calc(50% - var(--cal-w) / 2);
}
#calendar {
background: var(--bg);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: 0 4px 20px rgba(22, 24, 29, .06);
/* Pas de marge latérale : la grille va bord à bord et c'est la carte qui lui
sert de cadre, sinon les cases perdent 3rem de large et recoupent les titres. */
padding: clamp(.7rem, 2vw, 1.2rem) 0 0;
overflow: hidden;
}
#calendar .fc-header-toolbar { padding: 0 clamp(.7rem, 2vw, 1.2rem); }
/* Seule la bordure gauche doublait celle de la carte : celle du haut ferme le
tableau sous la barre d'outils, qui n'a rien au-dessus d'elle. */
#calendar .fc-scrollgrid { border-left-width: 0; }
/* La locale fr de FullCalendar écrit « septembre 2026 » : ::first-letter plutôt
que capitalize, qui mettrait aussi une majuscule au « sept. » de la vue semaine. */
#calendar .fc-toolbar-title { font-size: 1.5rem; font-weight: 600; }
#calendar .fc-toolbar-title::first-letter { text-transform: uppercase; }
#calendar .fc-button { border-radius: 999px; padding: .35rem 1rem; text-transform: none; }
#calendar .fc-button-group .fc-button { border-radius: 0; }
#calendar .fc-button-group .fc-button:first-child { border-radius: 999px 0 0 999px; }
#calendar .fc-button-group .fc-button:last-child { border-radius: 0 999px 999px 0; }
/* La case d'un événement reprend les codes des cartes de l'agenda : une barre
épaisse à gauche dans sa couleur assombrie, et l'heure en bordeaux.
La barre est un ::before en background-color: inherit filtré, donc elle suit la
couleur de catégorie que le flux pose en style inline, sans table de couleurs
foncées à tenir à jour en face de data/categories.yaml. */
/* FullCalendar réserve 1em sous la pile d'événements d'un jour (17px ici, la
taille du texte du site), ce qui laisse un blanc bien plus grand que les 2px
qui séparent les cases de tout le reste. */
#calendar .fc-daygrid-day-events { margin-bottom: 2px; }
#calendar .fc-daygrid-event {
position: relative;
overflow: hidden;
border-radius: 8px;
padding: 3px 5px 3px 11px;
/* 2px tout autour, l'écart entre la case et les lignes de séparation.
Les quatre côtés écrits ensemble, et pas seulement le haut, parce que
FullCalendar ne pose ses 2px latéraux qu'aux extrémités de l'événement
(.fc-event-start / .fc-event-end) : un stage à cheval sur deux semaines
reprend le lundi collé à la ligne du jour. Et parce que son margin-top à lui
vaut 1px, qu'il faut écraser plutôt que compléter, sinon l'écart entre deux
cases empilées dépend de sa valeur et vaut 3px. Sur la case et non sur le
.fc-daygrid-event-harness qui l'enveloppe, dont la marge se replie dans la
hauteur de l'enveloppe et ne se voit pas. */
margin: 2px 2px 0;
}
#calendar .fc-daygrid-event::before {
content: "";
position: absolute;
left: 0; top: 0; bottom: 0;
width: 5px;
background-color: inherit;
filter: saturate(5.6) brightness(.78);
}
/* 🛜 : le flux le colle à la fin du titre (evenements.html). En vue mois
programme-calendrier.js l'en détache pour l'épingler dans le coin. */
#calendar .fc-daygrid-event.a-streaming { padding-right: 1.4em; }
#calendar .fc-strm {
position: absolute;
top: 3px; right: 4px;        /* 3px = la marge haute de la case */
display: flex;
align-items: center;
/* 1lh = une ligne de ce texte, donc la première ligne de la case, celle de
l'heure : le logo se centre dessus sans nombre magique. D'où l'emoji agrandi
par transform et non par font-size, qui changerait ce que vaut ce 1lh. */
height: 1lh;
transform: scale(.8);
transform-origin: right center;
}
/* Les noms de jour et les numéros sont des <a> : sans le reset de bootstrap ils
prenaient la couleur de lien du site, soit de l'orange sur les 30 cases. */
#calendar .fc-col-header-cell { font-weight: 600; padding: .4rem 0; }
#calendar .fc-col-header-cell-cushion,
#calendar .fc-daygrid-day-number,
#calendar .fc-list-day-text,
#calendar .fc-list-day-side-text { color: var(--ink); }
/* Le clignotement passe par l'orange du site. Une seule étape : faute de 0% et
   de 100%, les extrémités sont le fond de l'élément lui-même, quel qu'il soit —
   la case blanche de la grille comme le bandeau bleuté d'un jour en vue liste —
   et il le retrouve à la fin. Ce qui reste alors, c'est le cadre. */
@keyframes today-flash {
50% { background-color: var(--orange); }
}
/* Deux classes et non une : .flash clignote, posée au chargement et au clic sur
   « Aujourd'hui », et .flashing est le cadre, reposé à chaque rendu — revenir
   sur le mois courant le retrouve sans que rien ne reclignote. */
.fc .fc-day-today.flash,
.fc .fc-day-today.flash .fc-list-day-cushion {
animation: today-flash 0.6s ease-in-out 2;
}
.fc .fc-day-today.flashing {
/* outline plutôt que border : n'entre pas dans la taille de la case, donc la
   grille ne bouge pas. -1.5px centre le trait sur la ligne grise de la grille
   plutôt que de le poser à l'intérieur de la case, où les vignettes des
   événements (marge de 2px) le recouvraient à moitié. D'où le z-index : la
   moitié qui dépasse est peinte par-dessus les cases voisines, qui sinon la
   recouvrent à droite et en dessous, étant dessinées après. */
outline: 3px solid var(--orange);
outline-offset: -1.5px;
position: relative;
z-index: 1;
}
/* Sur les bords extérieurs de la grille, ce débord tombait dans le vide :
   .fc-scroller-harness coupe à 1px en deçà de la .fc-scrollgrid, c'est-à-dire
   exactement là où commence la ligne grise du bord droit (et du bas), si bien
   que le trait s'arrêtait avant elle au lieu de la couvrir — visiblement plus
   fin que les trois autres côtés un dimanche, dernière colonne de la semaine.
   Rien ne défile ici (height: 'auto'), donc ce clip n'a rien à retenir. Ce qui
   dépasse encore est repris par l'overflow: hidden de la carte, dont la boîte de
   remplissage s'arrête justement au bord extérieur de cette ligne grise. */
#calendar .fc-scroller-harness { overflow: visible; }
/* En vue liste, la journée est un <tr> d'en-tête suivi d'un <tr> par événement
   (classés par programme-calendrier.js) : le cadre les englobe tous, donc il est
   dessiné côté par côté plutôt qu'en outline, qui enfermerait l'en-tête seul.
   Tout en ombres et non en bordures : la couleur de catégorie est posée en style
   inline sur le <tr> (eventDidMount) et ce fond de ligne se peint par-dessus les
   bordures fusionnées des cellules, qui disparaissaient. Et une ombre se décale,
   ce qu'une bordure ne fait pas : 1,5px dehors et 1,5px dedans centrent les
   traits du haut et du bas sur la ligne grise du tableau, comme en vue mois.
   D'où le z-index, le débord passant sinon sous la ligne voisine, peinte après. */
#calendar tr.fc-list-day.flashing,
#calendar .fc-list-day.flashing .fc-list-day-cushion { outline: 0; }
/* Sur le <th> et non sur le cushion qu'il contient : un bloc positionné dans une
   cellule de tableau cesse de s'étirer à sa hauteur, et le cadre ne couvrait plus
   que la ligne de texte. */
#calendar .fc-list-day.flashing > th,
#calendar tr.flashing-day > td { position: relative; z-index: 1; }
/* Haut et bas : 1,5px dehors, 1,5px dedans. Les moitiés intérieures des côtés
   sont ici aussi : le fond du cushion se peint par-dessus les pseudo-éléments du
   <th> qui le portent, et mangeait la moitié du trait. */
#calendar .fc-list-day.flashing .fc-list-day-cushion {
box-shadow: inset 0 1.5px var(--orange), 0 -1.5px var(--orange),
inset 1.5px 0 var(--orange), inset -1.5px 0 var(--orange);
}
/* Le trait du bas est un pseudo-élément par cellule, pour les deux raisons qui
   valent déjà pour les traits verticaux plus bas : WebKit ne peint pas de
   box-shadow sur un <tr> (le trait était invisible sur iPhone), et une ombre
   interne se dessine en deçà de la bordure grise fusionnée, qui la coupait en
   deux. Les slots libres : ::after sur la première cellule (son ::before est le
   trait de gauche), ::before sur les autres (le ::after de la dernière est le
   trait de droite). */
#calendar tr.flashing-day-last > td:first-child::after,
#calendar tr.flashing-day-last > td:not(:first-child)::before {
content: "";
position: absolute;
left: -1.5px;
right: -1.5px;
bottom: -1.5px;
height: 3px;
background: var(--orange);
}
/* Les traits verticaux sont des pseudo-éléments et non des ombres internes : une
   ombre interne se dessine dans la boîte de padding, donc en deçà de la bordure
   grise de la liste, et la ligne de séparation entre le jour et son événement,
   fusionnée par le tableau, se peignait par-dessus et la coupait. Ceux-ci
   débordent d'1,5px de chaque côté, centrés sur la bordure, et d'1px en haut et
   en bas, où ils passent au-dessus de la séparation. */
#calendar .fc-list-day.flashing > th::before,
#calendar .fc-list-day.flashing > th::after,
#calendar tr.flashing-day > td:first-child::before,
#calendar tr.flashing-day > td:last-child::after {
content: "";
position: absolute;
top: -1px;
bottom: -1px;
width: 3px;
background: var(--orange);
}
#calendar .fc-list-day.flashing > th::before,
#calendar tr.flashing-day > td:first-child::before { left: -1.5px; }
#calendar .fc-list-day.flashing > th::after,
#calendar tr.flashing-day > td:last-child::after { right: -1.5px; }
/* Le week-end est grisé, jour courant compris : ce qui reste après le
   clignotement est le cadre orange sur le fond ordinaire de la case, et le
   dimanche le fond ordinaire est ce gris. Sans !important, donc, et porté à la
   place par #calendar, qui suffit à passer devant le .fc .fc-daygrid-day
   .fc-day-today de FullCalendar : une animation perd contre un !important, et
   c'est pour cela que la règle excluait le jour courant plutôt que de le teinter
   — le clignotement se peignait en dessous. Une déclaration ordinaire, elle,
   laisse l'animation passer devant, puis la case retombe sur le gris.
   :not(.fc-day-other) : les jours des mois voisins gardent leur fond bleuté. */
#calendar .fc-day-sat:not(.fc-day-other),
#calendar .fc-day-sun:not(.fc-day-other) {
background-color: hsla(0, 0%, 84%, .3);
}
/* Les jours des mois voisins (showNonCurrentDates) : le fond bleuté que
   FullCalendar donnait aux cases vides quand ils étaient masqués, et le numéro
   grisé (0.5 au lieu de son 0.3, illisible). Les événements sont grisés un par
   un (.cal-hors-mois, programme-calendrier.js) et non par la pile de la case :
   une opacité sur la pile en fait un contexte d'empilement, et le fond de la
   case suivante recouvrait la retraite qui en déborde. */
#calendar .fc-day-other { background-color: var(--fc-neutral-bg-color); }
#calendar .fc-day-other .fc-daygrid-day-top,
#calendar .cal-hors-mois { opacity: .5; }
.fc-daygrid-event .fc-event-title {
/* multi-line */
white-space: normal !important;
overflow: visible !important;
text-overflow: clip !important;
word-break: break-word;
font-weight: 500;
}
/* Même heure que sur les cartes de l'agenda (.sw-time dans activites.js) :
même taille, même graisse, même bordeaux. */
.fc-daygrid-block-event .fc-event-time {
font-size: .82rem;
font-weight: 600;
color: var(--rose);
}
.fc-h-event .fc-event-main-frame {
display: block;
}
/* Le lieu d'un événement sur plusieurs jours, sous le titre (programme-calendrier.js). */
#calendar .fc-lieu { font-size: .82rem; font-weight: 400; }
/* La légende est le filtre : une pastille par catégorie, dans la couleur de
ses événements, dessinée par programme-calendrier.js depuis window.CATEGORIES
(data/categories.yaml). Elle a remplacé les cinq vignettes écrites à la main
ici et le menu « Sources » resté en display:none depuis Squarespace. */
.cal-legende {
display: flex;
flex-wrap: wrap;
gap: .4rem;
margin: 0 0 1rem;
}
.cal-legende label {
display: inline-flex;
transition: opacity .15s, filter .15s;
align-items: center;
gap: .35rem;
cursor: pointer;
font-size: .9rem;
color: #000;
border-radius: 999px;
padding: .2rem .8rem;
}
.cal-legende input { accent-color: var(--orange); }
.cal-legende label:not(.cal-gratuit):has(input:not(:checked)) { filter: grayscale(1); opacity: .5; }
/* Pas une catégorie : pas de couleur de catégorie, et poussé tout à droite. */
.cal-legende .cal-gratuit {
margin-left: auto;
background: var(--ice);
border: 1px solid var(--line);
}
.toolloc {
text-align: center;
padding-top: 10px;
}
.tippy-content {
padding: 20px;
}
/* La photo flotte : sans ça la bulle se replie sous elle quand le texte est court. */
.tippy-content::after {
content: "";
display: block;
clear: both;
}
/* La photo de l'activité en haut à droite de la bulle, les portraits en rond
à cheval sur son bord, comme sur les cartes de l'agenda (activites.js). */
.cal-tip-media {
position: relative;
float: right;
width: 38%;
max-width: 150px;
margin: 0 0 1.6rem .9rem;
}
.cal-tip-media > a { display: block; }
.cal-tip-img {
width: 100%;
height: auto;
border-radius: 12px;
display: block;
}
.cal-tip-profs {
position: absolute;
right: 6px;
bottom: -18px;
display: flex;
}
.cal-tip-prof {
width: 40px;
height: 40px;
object-fit: cover;
border-radius: 50%;
border: 3px solid #fff;
box-shadow: 0 4px 10px rgba(22, 24, 29, .2);
}
.cal-tip-prof + .cal-tip-prof {
margin-left: -12px;
}
@media (max-width: 480px) {
.cal-tip-media { width: 34%; margin-bottom: 1.2rem; }
.cal-tip-prof { width: 32px; height: 32px; border-width: 2px; }
.cal-tip-prof + .cal-tip-prof { margin-left: -10px; }
}
.tippy-box[data-theme~="light"] {
color: #000000;
box-shadow: 0 0 20px 4px rgba(154, 161, 177, 0.15), 0 4px 80px -8px rgba(36, 40, 47, 0.25),
0 4px 4px -2px rgba(91, 94, 105, 0.15);
background-color: #fff;
border-radius: var(--radius);
}
.tippy-box[data-theme~="light"][data-placement^="top"]>.tippy-arrow:before {
border-top-color: #fff;
}
.tippy-box[data-theme~="light"][data-placement^="bottom"]>.tippy-arrow:before {
border-bottom-color: #fff;
}
.tippy-box[data-theme~="light"][data-placement^="left"]>.tippy-arrow:before {
border-left-color: #fff;
}
.tippy-box[data-theme~="light"][data-placement^="right"]>.tippy-arrow:before {
border-right-color: #fff;
}
.tippy-box[data-theme~="light"]>.tippy-backdrop {
background-color: #fff;
}
.tippy-box[data-theme~="light"]>.tippy-svg-arrow {
fill: #fff;
}
.pgcal-container table {
margin: 0px;
padding: 0px;
}
.fc-event-past {
opacity: 50%;
}
.fc-h-event .fc-event-main-frame {
display: block;
}
/* Remove <a underlining for day and day of week */
.fc-col-header-cell-cushion,
.fc-daygrid-day-number {
text-decoration: none !important;
}
.fc-list-event-dot {
display: none !important;
}
/* Vues Semaine et Jour : la même heure que dans la grille du mois. Et le titre
y est un <a>, donc `.prose a` (700) le met en gras — le même piège que sur les
cartes de l'agenda, qui se le retirent pour la même raison. */
#calendar .fc-list-event-time {
font-size: .82rem;
font-weight: 600;
color: var(--rose);
white-space: nowrap;
}
/* Séance annulée : la case est déjà grise (la couleur vient du flux), il reste
« ANNULÉ » et le titre biffé. En CSS sur la classe que FullCalendar pose lui-même
(eventClassNames), et non en innerHTML dans eventDidMount, qui dédoublait le
titre en vue liste au deuxième rendu. En vue mois la classe est sur la case, en
vue liste sur la ligne : le titre est un descendant dans les deux. Le 🛜 de la
grille du mois n'est pas biffé, il est hors du titre. */
#calendar .cal-annule .fc-event-title::before,
#calendar .cal-annule .fc-list-event-title::before {
content: "ANNULÉ";
font-weight: 700;
/* inline-block : la biffure du titre ne s'y propage pas, et « ANNULÉ » reste
lisible — l'ancien code le tenait hors du <s> pour la même raison. La marge
plutôt qu'une espace dans le content, qui se réduit dans un inline-block. */
display: inline-block;
margin-right: .35em;
}
#calendar .cal-annule .fc-event-title,
#calendar .cal-annule .fc-list-event-title a { text-decoration: line-through; }
/* Pas de soulignement au survol : il courait sous le 🛜 de fin de titre et sous
l'espace qui le précède. Le retirer plutôt que de sortir l'emoji du texte, qui
est ce qui dédoublait le titre (voir programme-calendrier.js). La grille du mois
n'en a pas non plus, et la ligne reste un lien, curseur compris. */
#calendar .fc-list-event-title a { font-weight: 400; text-decoration: none; }
/* L'heure est plus petite que le titre : sans ça elle se calait en haut de la
ligne, `site.css` mettant tous les td en vertical-align: top. */
#calendar .fc-list-event td { vertical-align: middle; }
/* Allows wrapping of titles */
.fc-event-title {
white-space: normal;
}
.pgcal-branding {
color: grey;
font-size: 70%;
float: right;
}
@media (max-width: 767px) {
.fc-toolbar.fc-header-toolbar {
display: flex;
flex-direction: column;
/* Les trois blocs de la barre s'empilent ici, et l'intervalle de dates se
retrouve pris en sandwich entre les deux rangées de boutons, collé aux deux.
Un gap sur la colonne plutôt qu'une marge sur le titre : les écarts sont les
mêmes au-dessus et en dessous sans qu'il faille les écrire deux fois, et sur
une largeur d'écran où la barre reste en ligne, il ne s'applique pas. */
gap: .6rem;
}
}
