html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } h1:focus { outline: none; } a, .btn-link { color: #0071c1; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; } .content { padding-top: 1.1rem; } .valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; } .invalid { outline: 1px solid red; } .validation-message { color: red; } #blazor-error-ui { color-scheme: light only; background: lightyellow; bottom: 0; box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); box-sizing: border-box; display: none; left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem; position: fixed; width: 100%; z-index: 1000; } #blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; } .blazor-error-boundary { background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; padding: 1rem 1rem 1rem 3.7rem; color: white; } .blazor-error-boundary::after { content: "An error has occurred." } .loading-progress { position: absolute; display: block; width: 8rem; height: 8rem; inset: 20vh 0 auto 0; margin: 0 auto 0 auto; } .loading-progress circle { fill: none; stroke: #e0e0e0; stroke-width: 0.6rem; transform-origin: 50% 50%; transform: rotate(-90deg); } .loading-progress circle:last-child { stroke: #1b6ec2; stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; transition: stroke-dasharray 0.05s ease-in-out; } .loading-progress-text { position: absolute; text-align: center; font-weight: bold; inset: calc(20vh + 3.25rem) 0 auto 0.2rem; } .loading-progress-text:after { content: var(--blazor-load-percentage-text, "Loading"); } code { color: #c02d76; } .form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { color: var(--bs-secondary-color); text-align: end; } .form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { text-align: start; } /* ------------------------------------------------------------------ MaBibli — interface du catalogue (phase 3) Conçue mobile d'abord : une seule colonne, cibles tactiles d'au moins 2,75 rem, aucune règle de largeur en dessous du point de rupture. Le PC n'ajoute qu'une largeur maximale et une grille de cartes. ------------------------------------------------------------------ */ :root { --mb-accent: #1b3a5c; --mb-bord: #dcdcdc; --mb-fond-doux: #f4f5f7; --mb-texte-doux: #5c6470; --mb-danger: #a4242c; } body { background: #fff; color: #1d2129; } .titre-page { font-size: 1.4rem; margin: 0 0 0.75rem; } .message-discret { color: var(--mb-texte-doux); font-size: 0.9rem; } .message-erreur { background: #fdecec; border-left: 3px solid var(--mb-danger); color: #7a1a20; padding: 0.6rem 0.75rem; border-radius: 4px; margin: 0.75rem 0; } .message-avertissement { background: #fff8e1; border-left: 3px solid #c99700; color: #6b5200; padding: 0.6rem 0.75rem; border-radius: 4px; margin: 0.5rem 0; font-size: 0.9rem; } .compteur { color: var(--mb-texte-doux); font-size: 0.85rem; margin: 0.75rem 0 0.25rem; } /* --- Champs de saisie --- */ .barre-recherche { margin-bottom: 0.75rem; } .champ { display: block; margin-bottom: 0.9rem; border: 0; padding: 0; } .champ-libelle { display: block; font-size: 0.85rem; font-weight: 600; margin-bottom: 0.3rem; } .champ-aide, .obligatoire { font-weight: 400; color: var(--mb-texte-doux); } .champ-saisie { width: 100%; /* 16px minimum : en dessous, iOS zoome au focus. */ font-size: 1rem; padding: 0.7rem 0.75rem; border: 1px solid var(--mb-bord); border-radius: 8px; background: #fff; } .champ-saisie:focus { outline: 2px solid var(--mb-accent); outline-offset: -1px; } /* --- Segments (filtres, format, statut) --- */ .segments { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.5rem; } .segment { flex: 1 1 auto; min-height: 2.5rem; padding: 0.4rem 0.7rem; font-size: 0.85rem; border: 1px solid var(--mb-bord); border-radius: 999px; background: #fff; color: #1d2129; cursor: pointer; } .segment-actif { background: var(--mb-accent); border-color: var(--mb-accent); color: #fff; } .segments-compacts .segment { min-height: 2.2rem; font-size: 0.8rem; } .segment.statut-a-lire.segment-actif { background: #6b4fa8; border-color: #6b4fa8; } .segment.statut-en-cours.segment-actif { background: #b06b00; border-color: #b06b00; } .segment.statut-lu.segment-actif { background: #1f7a44; border-color: #1f7a44; } /* --- Boutons --- */ .bouton { display: inline-flex; align-items: center; justify-content: center; min-height: 2.75rem; padding: 0.6rem 1rem; border-radius: 8px; border: 1px solid var(--mb-bord); background: #fff; color: #1d2129; font-size: 0.95rem; text-decoration: none; cursor: pointer; } .bouton:disabled { opacity: 0.55; cursor: default; } .bouton-principal { background: var(--mb-accent); border-color: var(--mb-accent); color: #fff; } .bouton-discret { background: var(--mb-fond-doux); } .bouton-danger { background: var(--mb-danger); border-color: var(--mb-danger); color: #fff; } .actions-formulaire { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1rem 0; } .actions-flottantes { position: fixed; left: 0; right: 0; bottom: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0.6rem 1rem calc(0.6rem + env(safe-area-inset-bottom)); background: rgba(255, 255, 255, 0.96); border-top: 1px solid var(--mb-bord); } /* Base de 8rem : au-delà de trois entrées la barre passe sur deux lignes plutôt que de comprimer chaque libellé sur trois lignes de texte. Mesuré sur 375 px de large. */ .actions-flottantes .bouton { flex: 1 1 8rem; } /* --- Couvertures --- */ .couverture { position: relative; flex: 0 0 auto; overflow: hidden; border-radius: 6px; background: var(--mb-fond-doux); border: 1px solid var(--mb-bord); } .couverture-petite { width: 3.5rem; height: 5.25rem; } .couverture-moyenne { width: 6rem; height: 9rem; } .couverture-substitut { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; font-weight: 700; color: #9aa2ad; } .couverture-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; } /* Posée par l'attribut onerror : les couvertures OpenLibrary renvoient des 502 intermittents, l'image cassée laisse place au substitut. */ .couverture-image.image-cassee { display: none; } /* --- Liste de livres --- */ .liste-livres, .liste-candidats { list-style: none; margin: 0; padding: 0; } .carte-livre, .carte-candidat { display: flex; gap: 0.75rem; padding: 0.75rem 0; border-bottom: 1px solid var(--mb-bord); } .carte-corps { min-width: 0; flex: 1; } .carte-titre { display: block; font-weight: 600; color: #1d2129; text-decoration: none; margin: 0 0 0.15rem; } .carte-auteur { margin: 0 0 0.25rem; color: var(--mb-texte-doux); font-size: 0.9rem; } .carte-details { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin: 0 0 0.5rem; font-size: 0.8rem; color: var(--mb-texte-doux); } .etiquette { background: var(--mb-fond-doux); border-radius: 4px; padding: 0.1rem 0.4rem; } /* --- Choix entre plusieurs notices --- */ /* Les candidats partagent titre et auteur : ce sont l'éditeur et l'année qui les départagent, donc ils sont mis en avant visuellement. */ .candidat-distinction { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 0.4rem; font-size: 0.9rem; } .candidat-editeur { font-weight: 600; } .candidat-annee { background: #eef2f7; border-radius: 4px; padding: 0.05rem 0.4rem; } /* --- Formulaire --- */ .formulaire-apercu { display: flex; justify-content: center; margin-bottom: 1rem; } .zone-danger { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--mb-bord); } /* --- PC : simple élargissement, la structure mobile ne change pas. --- */ @media (min-width: 48rem) { .actions-flottantes { position: static; padding: 0; margin-top: 1.5rem; background: none; border-top: 0; } .actions-flottantes .bouton { flex: 0 0 auto; } } /* --- Prêts --- */ /* Le prêt est une information du foyer, pas de l'utilisateur : sa couleur est volontairement différente de celles des statuts de lecture, qui sont personnels. */ .etiquette-prete { background: #fdecd8; color: #7a4a00; font-weight: 600; } .bloc-prets { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--mb-bord); } .titre-section { font-size: 1.1rem; margin: 0 0 0.5rem; } .titre-sous-section { font-size: 0.95rem; margin: 1.25rem 0 0.35rem; color: var(--mb-texte-doux); } .bandeau-pret { background: #fff8ef; border-left: 3px solid #c98a2e; border-radius: 4px; padding: 0.6rem 0.75rem; margin: 0.5rem 0; font-size: 0.95rem; } .pret-duree { color: var(--mb-texte-doux); font-size: 0.85rem; white-space: nowrap; } .avertissement-fort { display: block; margin-top: 0.35rem; font-weight: 600; } /* L'historique se lit, il ne se manipule pas : rien n'y est cliquable. */ .liste-historique { list-style: none; margin: 0; padding: 0; font-size: 0.9rem; } .liste-historique li { padding: 0.4rem 0; border-bottom: 1px solid var(--mb-bord); } .historique-dates { display: block; color: var(--mb-texte-doux); font-size: 0.85rem; } /* Bouton d'action au sein d'une carte : il ne doit pas peser autant qu'un bouton de page, tout en gardant une cible tactile utilisable. */ .bouton-compact { min-height: 2.25rem; padding: 0.35rem 0.8rem; font-size: 0.85rem; } /* --- Scan du code-barres --- */ .scanner-cadre { position: relative; background: #000; border-radius: 8px; overflow: hidden; aspect-ratio: 4 / 3; } .scanner-video { width: 100%; height: 100%; object-fit: cover; display: block; } /* Le viseur matérialise la bande réellement décodée : viser ailleurs ne sert à rien. */ .scanner-viseur { position: absolute; left: 5%; right: 5%; top: 27.5%; height: 45%; border: 2px solid rgba(255, 255, 255, 0.85); border-radius: 6px; box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.35); } .scanner-mesure { font-variant-numeric: tabular-nums; } /* --- Agrandissement de la couverture --- */ /* Le déclencheur recouvre exactement la vignette. Il n'existe que quand il y a une image : sans couverture, le substitut à initiale reste un décor inerte. */ .couverture-declencheur { position: absolute; inset: 0; padding: 0; border: 0; background: none; cursor: zoom-in; } .calque-couverture { position: fixed; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center; padding: 1.5rem; background: rgba(0, 0, 0, 0.82); cursor: zoom-out; } .calque-couverture:focus { outline: none; } /* Bornée par la fenêtre : sur mobile la couverture occupe la largeur, sur PC elle ne dépasse pas la hauteur utile. */ .calque-image { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 6px; background: #fff; cursor: default; } .calque-image.image-cassee { display: none; } .calque-fermer { position: absolute; top: calc(0.5rem + env(safe-area-inset-top)); right: 0.5rem; min-width: 2.75rem; min-height: 2.75rem; border: 0; border-radius: 999px; background: rgba(255, 255, 255, 0.15); color: #fff; font-size: 1.1rem; cursor: pointer; } /* --- Statut de lecture en lecture seule --- */ /* Pastille pleine, aux couleurs des segments actifs, pour ne pas se confondre avec l'étiquette pâle du prêt : le statut est personnel, le prêt est celui du foyer. */ .etiquette-statut { color: #fff; font-weight: 600; } .etiquette-statut.statut-a-lire { background: #6b4fa8; } .etiquette-statut.statut-en-cours { background: #b06b00; } .etiquette-statut.statut-lu { background: #1f7a44; } /* --- Fiche du livre en lecture seule --- */ .couverture-grande { width: 8rem; height: 12rem; } .fiche-entete { display: flex; gap: 1rem; align-items: flex-start; margin-bottom: 1rem; } .fiche-infos { min-width: 0; flex: 1; } .fiche-titre { font-size: 1.15rem; margin: 0 0 0.25rem; } .fiche-auteurs { margin: 0 0 0.5rem; color: var(--mb-texte-doux); } /* Deux colonnes « libellé / valeur » qui restent lisibles à 320 px. */ .fiche-champs { margin: 0; font-size: 0.9rem; } .fiche-champs div { display: flex; gap: 0.5rem; padding: 0.15rem 0; } .fiche-champs dt { flex: 0 0 5rem; color: var(--mb-texte-doux); } .fiche-champs dd { margin: 0; min-width: 0; word-break: break-word; } /* Le statut reste manipulable hors édition : c'est le geste le plus fréquent de l'application, l'enfouir derrière « Éditer » serait un recul. */ .bloc-statut { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--mb-bord); } /* --- Auteurs --- */ /* Le nom d'un auteur est un point d'entrée, pas une décoration : il doit se voir comme cliquable, et rester une cible atteignable au pouce dans une liste dense. */ .lien-auteur { color: var(--mb-accent); text-decoration: none; padding: 0.1rem 0; } .lien-auteur:hover, .lien-auteur:focus { text-decoration: underline; } .liste-auteurs, .liste-rapprochements { list-style: none; margin: 0; padding: 0; } .carte-auteur-ligne { display: flex; gap: 0.5rem; align-items: center; justify-content: space-between; min-height: 2.75rem; padding: 0.4rem 0; border-bottom: 1px solid var(--mb-bord); } .rapprochements { margin-bottom: 1.5rem; padding: 0.75rem; border: 1px solid var(--mb-bord); border-radius: 8px; background: var(--mb-fond-doux); } .carte-rapprochement { padding: 0.5rem 0; border-top: 1px solid var(--mb-bord); } .rapprochement-noms { margin: 0; font-size: 0.95rem; }