/* Minimal stylesheet to accompany Tailwind CDN styles */ :root { color-scheme: dark; /* --- Layout metrics ----------------------------------------------------- Single source of truth for the app chrome height so templates never have to hardcode `top-16` / `h-[calc(100vh-4rem)]` (which silently break when the header wraps on narrow viewports). */ --header-h: 3.5rem; /* 56px — mobile */ --safe-top: 0px; --safe-bottom: 0px; --safe-left: 0px; --safe-right: 0px; /* Height actually consumed by the fixed header, safe-area included. `--header-measured` is published at runtime by HeaderComponent via a ResizeObserver: the header grows when the mobile search sheet opens, when an iOS safe area applies, or at high zoom, and a hardcoded value would let the sticky themes bar slide under it (it was fully hidden in that case). */ --header-offset: var(--header-measured, calc(var(--header-h) + var(--safe-top))); /* Usable viewport height that follows the mobile browser chrome. */ --app-vh: 100vh; } @supports (height: 100dvh) { :root { --app-vh: 100dvh; } } @media (min-width: 768px) { :root { --header-h: 4rem; } /* 64px — desktop */ } html, body { height: 100%; min-height: 100%; } html { /* `clip` (not `hidden`) so the element never becomes a scroll container — `overflow: hidden` on silently breaks every `position: sticky` child, including the header rails. */ overflow-x: clip; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-behavior: smooth; } @supports not (overflow: clip) { html { overflow-x: hidden; } } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } } body { margin: 0; min-height: var(--app-vh); font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji"; background-color: #0f172a; /* slate-900 */ color: #e2e8f0; /* slate-200 */ /* No grey flash / no accidental text selection while tapping chrome. */ -webkit-tap-highlight-color: transparent; overscroll-behavior-y: none; } /* Inputs on iOS render at 16px by default which triggers focus zoom; make sure no field ever drops below that on small viewports. Buttons are excluded so compact `text-xs` toolbars keep their compact look. */ @media (max-width: 767px) { input:not([type="checkbox"]):not([type="radio"]), select, textarea { font-size: max(16px, 1em); } /* Never let a flex/grid child blow out the viewport. */ img, svg, video, iframe { max-width: 100%; } } /* --- Touch ergonomics ----------------------------------------------------- */ /* WCAG 2.5.5 / Apple HIG: 44px is the comfortable minimum on coarse pointers. Desktop keeps the compact visual sizing. */ @media (pointer: coarse) { button, [role="button"], a.chip { min-height: 44px; } /* Icon-only controls (no text child) get a square minimum too. */ button.icon-only, [role="button"].icon-only { min-width: 44px; } } .chip { transition: background-color .15s ease, color .15s ease, opacity .15s ease; } /* Expand tiny hit areas without changing visual size. */ .hit-expand { position: relative; } .hit-expand::after { content: ""; position: absolute; top: 50%; left: 50%; min-width: 44px; min-height: 44px; transform: translate(-50%, -50%); } /* --- Mobile primitives ---------------------------------------------------- */ /* Horizontal swipe rail: the standard mobile pattern for tab strips that would otherwise wrap or get clipped. */ .scroll-rail { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; scrollbar-width: thin; overscroll-behavior-x: contain; } /* Scroll affordance for swipe rails that hide their scrollbar. */ .scroll-rail-fade { position: relative; } .scroll-rail-fade::after { content: ""; position: absolute; top: 0; bottom: 0; right: 0; width: 2rem; pointer-events: none; background: linear-gradient(to right, transparent, var(--bg)); opacity: 0.9; } /* Vertical stacks inside dialogs must be able to scroll within dvh. */ .dialog-scroll { max-height: min(85dvh, 100%); overflow-y: auto; overscroll-behavior: contain; } /* Prevent long unbroken strings (UUIDs, URLs, tokens) from breaking layout. */ .break-anywhere { overflow-wrap: anywhere; word-break: break-word; } /* Focus ring for keyboard users only (touch devices get :focus instead). */ .focus-ring:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 0.5rem; } /* --- App chrome primitives ------------------------------------------------ */ /* Icon-only header/button. 40px on pointer devices, 44px on touch. */ .app-icon-btn { display: inline-flex; align-items: center; justify-content: center; min-width: 2.5rem; min-height: 2.5rem; padding: 0.375rem; border-radius: 9999px; color: var(--text); transition: background-color .15s ease, opacity .15s ease; } .app-icon-btn:hover { background-color: var(--panel-2); } .app-icon-btn:active { transform: scale(.94); } /* Avatar trigger (always >= 44px of tappable area). */ .app-avatar-btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.25rem; border-radius: 9999px; transition: background-color .15s ease; } .app-avatar-btn:hover { background-color: var(--panel-2); } /* Circular user badge ("pastille"). Belt and braces: `rounded-full` from the markup plus these rules, so the badge can never degrade into a rectangle if a theme stylesheet overrides the radius. */ .avatar-circle { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 2rem; height: 2rem; min-width: 2rem; min-height: 2rem; aspect-ratio: 1 / 1; border-radius: 9999px !important; border-width: 2px; border-style: solid; font-size: 0.875rem; font-weight: 600; line-height: 1; overflow: hidden; text-transform: uppercase; } /* The fixed app header: honours the safe area and never overflows. */ .app-header { /* Nothing inside may widen the bar. */ max-width: 100vw; } /* --- Quick menu / command palette (Ctrl+K) -------------------------------- */ .quick-menu-backdrop { position: fixed; inset: 0; z-index: 9999; background-color: rgba(0, 0, 0, .6); display: flex; align-items: flex-start; justify-content: center; padding: max(1rem, env(safe-area-inset-top)) 1rem; overflow-y: auto; overscroll-behavior: contain; } .quick-menu-dialog { margin-top: 5vh; width: 100%; max-width: 56rem; max-height: min(85dvh, 100%); display: flex; flex-direction: column; overflow: hidden; border-radius: .75rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, .25); background-color: var(--bg, #0f172a); border: 1px solid var(--border, #334155); } @supports not (height: 100dvh) { .quick-menu-dialog { max-height: 85vh; } } /* Panneau de filtres : même coque que la palette Ctrl+K, plus étroit (les options sont des pastilles, pas des colonnes). */ .filter-panel-dialog { max-width: 44rem; } @media (max-width: 639px) { /* On phones the dialog owns the viewport: no floating "modal" look. */ .quick-menu-backdrop { padding: 0; } .quick-menu-dialog { margin-top: 0; max-height: 100dvh; height: 100dvh; border-radius: 0; border: 0; } } /* Container helper: responsive gutters + fluid cap (Tailwind's CDN `container` is pinned to a single width, which breaks small viewports). */ .container { width: 100%; margin-inline: auto; padding-inline: 1rem; max-width: 100%; } @media (min-width: 640px) { .container { padding-inline: 1.5rem; } } @media (min-width: 1024px) { .container { max-width: 1280px; padding-inline: 2rem; } } /* --- Theme System ------------------------------------------------------- */ /* Define CSS variables per theme and override a few common Tailwind utils */ /* Defaults (used as fallback) */ :root { --bg: #0f172a; /* dark slate */ --text: #e2e8f0; /* slate-200 */ --muted: #94a3b8; /* slate-400 */ --panel: #1f2937; /* slate-800 */ --panel-2: #334155; /* slate-700 */ --overlay: rgba(31,41,55,.5); --accent: #ef4444; /* red-500/600 */ --accent-hover: #dc2626; /* red-600/700 */ --border: #334155; --selection: rgba(255,255,255,.06); /* default subtle selection for dark */ } /* Dark */ [data-theme="dark"] { color-scheme: dark; /* Dark gray palette (softer than deep slate) */ --bg: #14171b; /* base background */ --text: #e5e7eb; /* slightly brighter text */ --muted: #a1a7b3; /* softer secondary text */ --panel: #1d2126; /* cards/headers */ --panel-2: #262b31; /* inputs/secondary panels */ --overlay: rgba(29,33,38,.6); --accent: #ef4444; /* unchanged accent */ --accent-hover: #dc2626; --border: #2f3540; /* subtle border */ --selection: #2b3139; /* hover/selection on dark */ } /* Pure Black */ [data-theme="black"] { color-scheme: dark; --bg: #000000; --text: #e5e7eb; --muted: #9ca3af; --panel: #0a0a0a; --panel-2: #111111; --overlay: rgba(0,0,0,.6); --accent: #ef4444; --accent-hover: #dc2626; --border: #1f2937; --selection: #121416; } /* Light */ [data-theme="light"] { color-scheme: light; --bg: #f8fafc; /* slate-50 */ --text: #0f172a; /* slate-900 */ --muted: #64748b;/* slate-500 for secondary text */ --panel: #ffffff; --panel-2: #f1f5f9; /* slate-100 */ --overlay: #ffffff; /* solid white for cards/headers using bg-slate-800/50 */ --accent: #ef4444; --accent-hover: #dc2626; --border: #e2e8f0; --selection: #eef2f7; /* light mode selection (ref. YouTube style) */ } /* Blue (navy) */ [data-theme="blue"] { color-scheme: dark; --bg: #0b1220; --text: #dbeafe; /* blue-100 */ --muted: #93c5fd; /* blue-300 */ --panel: #111a2b; --panel-2: #17223a; --overlay: rgba(17,26,43,.6); --accent: #3b82f6; /* blue-500 */ --accent-hover: #2563eb; /* blue-600 */ --border: #1e3a8a; /* blue-900 */ --selection: #1a2440; } /* System: default to light, override when OS is dark */ [data-theme="system"] { color-scheme: light; --bg: #f8fafc; --text: #0f172a; --muted: #334155; --panel: #ffffff; --panel-2: #f1f5f9; --overlay: rgba(255,255,255,.8); --accent: #ef4444; --accent-hover: #dc2626; --border: #e2e8f0; --selection: #eef2f7; } @media (prefers-color-scheme: dark) { [data-theme="system"] { color-scheme: dark; --bg: #0f172a; --text: #e2e8f0; --muted: #94a3b8; --panel: #1f2937; --panel-2: #334155; --overlay: rgba(31,41,55,.5); --accent: #ef4444; --accent-hover: #dc2626; --border: #334155; --selection: #2b3139; } } /* Apply variables */ body { background-color: var(--bg); color: var(--text); } /* Mild overrides so Tailwind utilities adapt to themes without rebuild */ [data-theme] .bg-slate-900 { background-color: var(--bg) !important; } [data-theme] .bg-gray-800, [data-theme] .bg-slate-800 { background-color: var(--panel) !important; } [data-theme] .bg-slate-700 { background-color: var(--panel-2) !important; } [data-theme] .bg-slate-800\/50 { background-color: var(--overlay) !important; } [data-theme] .text-slate-200 { color: var(--text) !important; } [data-theme] .text-slate-100 { color: var(--text) !important; } [data-theme] .text-slate-300 { color: color-mix(in srgb, var(--text) 80%, transparent) !important; } [data-theme] .text-slate-400 { color: var(--muted) !important; } [data-theme] .text-white { color: var(--text) !important; } [data-theme] .border-slate-700 { border-color: var(--border) !important; } /* Accent buttons */ [data-theme] .bg-red-600 { background-color: var(--accent) !important; } [data-theme] .hover\:bg-red-500:hover { background-color: var(--accent-hover) !important; } [data-theme] .bg-gray-600 { background-color: var(--panel-2) !important; } [data-theme] .hover\:bg-gray-500:hover { background-color: var(--panel) !important; } /* Light-specific refinements */ [data-theme="light"] header { box-shadow: 0 2px 12px rgba(0,0,0,.06); } [data-theme="light"] .shadow-2xl { box-shadow: 0 25px 50px -12px rgba(0,0,0,.15) !important; } [data-theme="light"] .bg-black\/75 { /* make duration pill less heavy on light */ background-color: rgba(0,0,0,.5) !important; } /* Cards and panels separation in Light */ [data-theme="light"] .bg-slate-800, [data-theme="light"] .bg-gray-800, [data-theme="light"] .bg-slate-800\/50 { border: 1px solid var(--border) !important; box-shadow: 0 1px 2px rgba(0,0,0,.05); } /* Hover states for list/grid cards */ [data-theme="light"] .hover\:bg-slate-700\/50:hover, [data-theme="light"] .hover\:bg-slate-800:hover, [data-theme="light"] .hover\:bg-slate-800\/50:hover, [data-theme="light"] .hover\:bg-slate-700:hover, [data-theme="light"] .hover\:bg-gray-800:hover { background-color: var(--selection) !important; /* subtle hover */ } /* In light mode, also treat any active dark-slate background as selection */ [data-theme="light"] .bg-slate-800, [data-theme="light"] .bg-slate-800\/50, [data-theme="light"] .bg-gray-800 { background-color: var(--panel) !important; border: 1px solid var(--border) !important; } /* Inputs in light mode */ [data-theme="light"] input.bg-slate-700, [data-theme="light"] input.bg-gray-800, [data-theme="light"] select.bg-gray-800, [data-theme="light"] select.bg-slate-800, [data-theme="light"] textarea.bg-gray-800, [data-theme="light"] textarea.bg-slate-800 { background-color: var(--panel-2) !important; color: var(--text) !important; border: 1px solid var(--border) !important; } [data-theme="light"] input::placeholder, [data-theme="light"] textarea::placeholder { color: #94a3b8; /* slate-400 */ } /* Hide scrollbars on horizontal lists where we still want scrolling */ .no-scrollbar::-webkit-scrollbar { display: none; } .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; } /* --- Sidebar: scrollbar visible au survol / focus uniquement --------------- */ .sidebar-scroll { overflow-y: hidden; scrollbar-width: thin; scrollbar-color: #334155 transparent; scrollbar-gutter: stable; } .sidebar-scroll:hover, .sidebar-scroll:focus, .sidebar-scroll:focus-within { overflow-y: auto; } .sidebar-scroll::-webkit-scrollbar { width: 6px; } .sidebar-scroll::-webkit-scrollbar-track { background: transparent; } .sidebar-scroll::-webkit-scrollbar-thumb { background: #334155; border-radius: 9999px; } @media (hover: none) { /* tactile / mobile : toujours scrollable */ .sidebar-scroll { overflow-y: auto; } } @media (prefers-reduced-motion: reduce) { .sidebar-scroll { scroll-behavior: auto; } } /* --- Shorts player frame --------------------------------------------------- */ /* 9:16 frame constrained by BOTH the dynamic viewport height and the viewport width, so it never overflows a portrait phone nor a landscape one. `--shorts-chrome` reserves room for the provider rail + bottom navigation. */ .shorts-frame { --shorts-chrome: 11rem; aspect-ratio: 9 / 16; width: auto; max-width: 100%; height: min( calc(var(--app-vh) - var(--header-offset) - var(--shorts-chrome)), calc((100vw - 2rem) * 16 / 9) ); min-height: 0; } @media (min-width: 768px) { /* Bureau : pas de barre du bas, les flèches sont sur les côtés — on ne réserve que la scène (py-3) + la charge « Chargement… ». La vidéo gagne ~80 px de hauteur (et autant de largeur, 9:16 oblige). */ .shorts-frame { --shorts-chrome: 4rem; } } @media (max-width: 767px) { /* Mobile : quasi plein écran (seule la rangée de chips est réservée : la flèche retour et le chrome vivent EN OVERLAY dans le cadre). */ .shorts-frame { --shorts-chrome: 5.5rem; } } /* Embed PAYSAGE dans le cadre vertical : la boîte de l'iframe suit le16:9 (sinon le lecteur étire ou réduit la vidéo dans un cadre 9:16). */ .shorts-frame iframe.embed-wide { top: 50%; bottom: auto; height: auto; aspect-ratio: 16 / 9; transform: translateY(-50%); } /* --- Shorts immersive stage (façon YouTube) -------------------------------- */ /* Plein écran vertical : le cadre 9:16 flotte sur un fond d'ambiance tiré de la vignette courante. `100dvh` (barres navigateur mobiles) avec repli. */ .shorts-stage { min-height: calc(var(--app-vh, 100dvh) - var(--header-offset, 0px)); touch-action: pan-y; overscroll-behavior: contain; } .shorts-ambient { position: absolute; inset: 0; z-index: 0; overflow: hidden; background: #000; pointer-events: none; } .shorts-ambient img { width: 100%; height: 100%; object-fit: cover; filter: blur(48px) brightness(0.45); transform: scale(1.2); } @media (prefers-reduced-motion: no-preference) { .shorts-ambient img { transition: opacity 0.4s ease; } } /* Zone de méta basse : la safe-area des encoches ne doit jamais manger le titre ou le bouton S'abonner. */ .shorts-meta { padding-bottom: max(0.75rem, env(safe-area-inset-bottom)); } /* --- Shorts : chrome auto-hide + capteur tactile ---------------------------- */ /* Desktop : le chrome (pastilles, boutons, rail, méta) s'efface après 2,5 s d'immobilité de la souris et revient au moindre mouvement (façon YouTube). Sur tactile, `mousemove` ne tire presque jamais : tout reste visible. */ .shorts-chrome { transition: opacity 0.25s ease; } .shorts-chrome-hidden .shorts-chrome { opacity: 0; pointer-events: none; } /* Contrôles flottants (rangée de flèches + FAB menu) : presque fondu au repos pour laisser la vidéo respirer (ton pro), pleins dès le moindre mouvement de souris (chromeVisible) ou survol direct. Le panneau ouvert et le FAB ouvert ne sont jamais atténués. Le tactile ne déclenche presque pas mousemove : sur mobile tout reste donc plein (comportement voulu). */ .shorts-frame .shorts-ctrl, .shorts-frame app-player-menu .pm-fab { transition: opacity 0.25s ease; } .shorts-chrome-hidden .shorts-frame .shorts-ctrl { opacity: 0.18; } .shorts-chrome-hidden .shorts-frame .shorts-ctrl:not(:disabled):hover { opacity: 1; } .shorts-chrome-hidden .shorts-frame app-player-menu .pm-fab { opacity: 0.18; } .shorts-chrome-hidden .shorts-frame app-player-menu .pm-fab:hover, .shorts-chrome-hidden .shorts-frame app-player-menu .pm-fab.pm-fab-open { opacity: 1; } @media (prefers-reduced-motion: reduce) { .shorts-chrome { transition: none; } } /* Capteur tactile mobile : couche transparente AU-DESSUS de l'iframe (qui mangerait les touchers) mais SOUS le chrome. `touch-action: none` donne le geste à nos handlers (swipe = navigation, tap = play/pause ou accès lecteur) au lieu de scroller/zoomer la page. */ .shorts-gesture { touch-action: none; } /* --- Player FAB menu (shared /watch & /shorts) ---------------------------- */ /* Coque du menu flottant : pastille « verre dépoli », panneau en élévation avec en-tête, entrée/sortie animées. Harmonisé avec les commandes overlay des players YouTube/Dailymotion (fond sombre translucide, blur, ring fin). */ /* Pastille FAB (coin du lecteur) : disque compact en dégradé sombre, léger relief (ombre + highlight interne) et agrandissement au survol. */ .pm-fab { display: inline-flex; touch-action: none; /* glisser le FAB ne défile pas la page */ cursor: grab; align-items: center; justify-content: center; width: 2.25rem; /* 36px */ height: 2.25rem; border-radius: 9999px; color: #fff; background: linear-gradient(180deg, rgba(30, 41, 59, .88), rgba(2, 6, 23, .9)); border: 1px solid rgba(255, 255, 255, .18); box-shadow: 0 4px 14px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .12); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); cursor: pointer; transition: background .15s ease, border-color .15s ease, transform .15s ease, box-shadow .15s ease; } .pm-fab svg { width: 1rem; height: 1rem; opacity: .92; } .pm-fab:hover { background: linear-gradient(180deg, rgba(51, 65, 85, .95), rgba(15, 23, 42, .95)); border-color: rgba(255, 255, 255, .32); transform: scale(1.06); } .pm-fab:active { transform: scale(.94); cursor: grabbing; } .pm-fab-open { background: linear-gradient(180deg, rgba(51, 65, 85, .95), rgba(2, 6, 23, .95)); border-color: rgba(255, 255, 255, .32); } .pm-fab:focus-visible { outline: 2px solid rgba(255, 255, 255, .6); outline-offset: 2px; } /* Panneau déroulant (toujours monté, animé via open/close). */ .pm-panel { position: absolute; top: calc(100% + 0.5rem); left: 0; width: 15rem; /* 240px */ /* Borné à 65vh : le cadre /shorts a `overflow:hidden` — au-delà, le bas du menu serait rogné sur les petits écrans (le corps scrolle donc). */ max-height: min(65vh, 430px); display: flex; flex-direction: column; overflow: hidden; border-radius: 0.75rem; background: rgba(15, 23, 42, .94); /* slate-900/94 */ border: 1px solid rgba(255, 255, 255, .12); box-shadow: 0 20px 50px rgba(0, 0, 0, .6), 0 2px 10px rgba(0, 0, 0, .4), inset 0 1px 0 rgba(255, 255, 255, .07); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); opacity: 0; transform: translateY(-6px) scale(.97); transform-origin: top left; visibility: hidden; pointer-events: none; transition: opacity .18s ease, transform .18s cubic-bezier(.2, .9, .3, 1), visibility 0s linear .18s; } .pm-panel-open { opacity: 1; transform: translateY(0) scale(1); visibility: visible; pointer-events: auto; transition: opacity .18s ease, transform .18s cubic-bezier(.2, .9, .3, 1); } /* En-tête : titre + bouton fermer. */ .pm-panel-header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.4375rem 0.4375rem 0.4375rem 0.75rem; border-bottom: 1px solid rgba(255, 255, 255, .08); flex-shrink: 0; } .pm-panel-title { font-size: 0.75rem; /* 12px */ font-weight: 600; letter-spacing: .02em; color: rgba(255, 255, 255, .92); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pm-panel-close { display: inline-flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem; border-radius: 9999px; color: rgba(255, 255, 255, .7); flex-shrink: 0; transition: background-color .15s ease, color .15s ease; } .pm-panel-close svg { width: 0.875rem; height: 0.875rem; } .pm-panel-close:hover { background: rgba(255, 255, 255, .1); color: #fff; } .pm-panel-close:focus-visible { outline: 2px solid rgba(255, 255, 255, .5); outline-offset: 1px; } /* Corps scrollable du panneau. */ .pm-panel-body { padding: 0.375rem; overflow-y: auto; overscroll-behavior: contain; flex: 1; min-height: 0; } .pm-panel-body::-webkit-scrollbar { width: 6px; } .pm-panel-body::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .18); border-radius: 9999px; } /* Libellé de section. */ .pm-section-label { padding: 0.5rem 0.625rem 0.25rem; font-size: 0.6875rem; /* 11px */ font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); } /* Élément de menu. */ .pm-item { display: flex; align-items: center; gap: 0.625rem; width: 100%; padding: 0.5rem 0.625rem; border-radius: 0.5rem; font-size: 0.8125rem; /* 13px */ color: var(--text); text-align: left; line-height: 1.3; cursor: pointer; transition: background-color .12s ease, color .12s ease; } .pm-item:hover { background-color: rgba(255, 255, 255, .08); } .pm-item:active { background-color: rgba(255, 255, 255, .12); } .pm-item:focus-visible { outline: 2px solid rgba(255, 255, 255, .35); outline-offset: -2px; } .pm-item:disabled { opacity: .4; cursor: not-allowed; } .pm-item svg { flex-shrink: 0; color: rgba(255, 255, 255, .75); } .pm-item .pm-item-label { flex: 1; min-width: 0; } .pm-item .pm-item-hint { font-size: 0.6875rem; color: var(--muted); } /* Séparateur. */ .pm-sep { height: 1px; margin: 0.25rem 0.5rem; background: rgba(255, 255, 255, .08); } /* /shorts : la flèche « retour accueil » occupe le coin haut-gauche (mobile) et passait PAR-DESSUS le FAB (elle est à z-20 au niveau de la scène, le FAB étant enfermé dans le contexte z-10). Le FAB passe donc en haut à droite du cadre, panneau ancré sur le même côté. */ .shorts-frame app-player-menu { left: auto; right: 0.5rem; } .shorts-frame app-player-menu .pm-panel { left: auto; right: 0; transform-origin: top right; } /* FAB glissé ailleurs que son coin d'origine : le panneau s'ouvre TOUJOURS vers l'intérieur du conteneur (sinon `overflow: hidden` le rogne) et vers le haut quand le FAB est en bas. Les classes sont posées par PlayerMenuComponent.applyAnchor(). */ app-player-menu.pm-opens-left .pm-panel { left: auto; right: 0; transform-origin: top right; } app-player-menu.pm-opens-right .pm-panel { left: 0; right: auto; transform-origin: top left; } app-player-menu.pm-opens-up .pm-panel { top: auto; bottom: calc(100% + 0.5rem); transform-origin: bottom left; } app-player-menu.pm-opens-left.pm-opens-up .pm-panel { transform-origin: bottom right; } /* Bureau : menu plus compact pour laisser la place à la vidéo (shorts). */ @media (min-width: 768px) { .shorts-frame app-player-menu .pm-fab { width: 1.875rem; height: 1.875rem; } .shorts-frame app-player-menu .pm-fab svg { width: 0.875rem; height: 0.875rem; } .shorts-frame app-player-menu .pm-panel { width: 13rem; max-height: min(60vh, 380px); } .shorts-frame app-player-menu .pm-item { padding: 0.375rem 0.5rem; gap: 0.5rem; font-size: 0.75rem; } .shorts-frame app-player-menu .pm-section-label { padding: 0.375rem 0.5rem 0.125rem; font-size: 0.625rem; } .shorts-frame app-player-menu .pm-panel-header { padding: 0.3125rem 0.3125rem 0.3125rem 0.625rem; } } /* --- Drawer / overlay plumbing -------------------------------------------- */ /* Applied to while a full-screen drawer or modal owns the viewport. Note: deliberately NOT `touch-action: none` — touch-action is intersected up the ancestor chain, so it would also freeze scrolling *inside* the drawer. */ body.scroll-locked { overflow: hidden; } /* Skip link: visible only on keyboard focus. */ .skip-link { position: absolute; left: 0.5rem; top: -4rem; z-index: 100; padding: 0.5rem 0.875rem; border-radius: 0.5rem; background: var(--accent); color: #fff; font-size: 0.875rem; font-weight: 600; transition: top .15s ease; } .skip-link:focus { top: 0.5rem; }