mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
ba70ad28b8
Arabic runs the app right to left, but margins, paddings, insets, text alignment, borders and corner radii were written for the left side, so icons sat against the wrong edge of their labels, indents and badges pointed the wrong way and amount columns aligned left. They now use the logical forms (ms-, ps-, start-, text-start, marginInlineStart, insetInlineEnd and their CSS equivalents), which are the very same sides in a left to right language. Geometry stays physical on purpose: maps, positions measured from the pointer or a bounding box, time axes, sliders, transforms and print output. Amounts drawn as several spans keep their order in a right to left row, the time picker keeps hours before minutes, and two switches whose thumb slid out of its track in Arabic now move the right way. lint:rtl counts what is still physical per file against a baseline that may only shrink (1512 uses in 370 files before, 319 in 113 now), and CI runs it, so new code reaches for the logical forms.
1612 lines
63 KiB
CSS
1612 lines
63 KiB
CSS
@tailwind base;
|
||
@tailwind components;
|
||
@tailwind utilities;
|
||
|
||
html { height: 100%; overflow: hidden; background-color: var(--bg-primary); }
|
||
body { height: 100%; overflow: auto; overscroll-behavior: none; -webkit-overflow-scrolling: touch; }
|
||
|
||
/* Phone (<768px): the document is the scroller, not body (#1809). iOS Safari
|
||
only minimises its address bar in reaction to the root scroller moving, so a
|
||
locked html keeps the bar at full height forever. `visible` on both, not
|
||
`auto`: only then does body's overflow propagate to the viewport, which is
|
||
what lets the shared body scroll lock stop the page behind an overlay.
|
||
overscroll-behavior has to sit on html, because the value on body is never
|
||
propagated to the viewport, and without it a pull at the top of the page
|
||
reloads the installed PWA. Desktop/tablet keep the rules above. */
|
||
@media (max-width: 767px), (pointer: coarse) and (max-height: 500px) {
|
||
html { overflow: visible; overscroll-behavior: none; }
|
||
/* overflow-x clip (not hidden: clip creates no scroll container) so a screen
|
||
that accidentally overflows sideways cannot widen the document. Android's
|
||
forced-zoom setting reacts to a wider document by growing the layout
|
||
viewport that position:fixed resolves against, which pushed the top bar
|
||
and the dock off the right edge on narrow phones. On body, never on html:
|
||
body's overflow only propagates to the viewport while html stays visible,
|
||
and the shared body scroll lock relies on that propagation. */
|
||
body { height: auto; overflow: visible; overflow-x: clip; }
|
||
/* Transparent so the shell's own background layer, which sits behind the page
|
||
on a negative z-index, is not painted over: a negative layer goes above the
|
||
canvas but below the background of ordinary blocks, and body is one. The
|
||
alternative, lifting the layer into the positioned range, would have turned
|
||
the content wrapper into a stacking context and trapped every fixed overlay
|
||
inside it (the vacay FAB ended up under the dock that way).
|
||
|
||
`html body` and not `body`: a media query adds no specificity, and the
|
||
themed `body` rule further down this file would otherwise win on source
|
||
order alone and paint over the layer again. */
|
||
html body { background-color: transparent; }
|
||
}
|
||
|
||
|
||
/* Journey desktop feed: hide scrollbar (right column is a sticky map, a
|
||
visible scrollbar on the left breaks the polarsteps-style reading feel). */
|
||
.journey-feed-scroll { scrollbar-width: none; -ms-overflow-style: none; }
|
||
.journey-feed-scroll::-webkit-scrollbar { display: none; }
|
||
|
||
/* Road trip rail: hide the scrollbar of the day list. The totals card is pinned
|
||
above the list and therefore outside its scroll box, so a visible scrollbar
|
||
would take width from the day cards alone and leave the two stacks a few
|
||
pixels out of line — the one thing a column of nested cards cannot hide. */
|
||
.roadtrip-rail-scroll { scrollbar-width: none; -ms-overflow-style: none; }
|
||
.roadtrip-rail-scroll::-webkit-scrollbar { display: none; }
|
||
|
||
/* Journey video: let the player fill the box the lightbox gives it.
|
||
*
|
||
* Plyr sizes its wrapper from the video's intrinsic aspect ratio via a
|
||
* padding-bottom hack, which fights a fixed-height container — a portrait clip
|
||
* then overflowed and pushed its own controls out of reach (#2041). Pinning the
|
||
* wrapper to the box and letting the video letterbox inside it keeps any aspect
|
||
* ratio whole and the control bar on screen. */
|
||
.trek-video-player .plyr,
|
||
.trek-video-player .plyr__video-wrapper {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #000;
|
||
}
|
||
.trek-video-player .plyr__video-wrapper {
|
||
padding-bottom: 0 !important;
|
||
}
|
||
.trek-video-player video {
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
object-fit: contain !important;
|
||
}
|
||
|
||
/* Leaflet Popups — Enter-Animation vom Anchor-Tip */
|
||
.leaflet-popup {
|
||
animation: trek-popover-enter 220ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
transform-origin: bottom center;
|
||
will-change: transform, opacity;
|
||
}
|
||
.leaflet-popup-content-wrapper {
|
||
border-radius: 14px !important;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18) !important;
|
||
background: var(--bg-card) !important;
|
||
color: var(--text-primary) !important;
|
||
border: 1px solid var(--border-faint);
|
||
}
|
||
.leaflet-popup-tip {
|
||
background: var(--bg-card) !important;
|
||
}
|
||
.leaflet-popup-close-button {
|
||
transition: color 150ms cubic-bezier(0.23, 1, 0.32, 1), transform 150ms cubic-bezier(0.23, 1, 0.32, 1) !important;
|
||
}
|
||
.leaflet-popup-close-button:hover {
|
||
transform: scale(1.15);
|
||
color: var(--text-primary) !important;
|
||
}
|
||
|
||
/* GL hover popup — the name/category/address card on marker hover.
|
||
Matches the Leaflet map's white hover tooltip. pointer-events:none so moving
|
||
onto the popup never steals the marker's mouseleave and causes flicker. */
|
||
.trek-map-popup { pointer-events: none; }
|
||
.trek-map-popup .mapboxgl-popup-content,
|
||
.trek-map-popup .maplibregl-popup-content {
|
||
padding: 7px 10px;
|
||
border-radius: 10px;
|
||
background: #fff;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.16);
|
||
}
|
||
/* No speech-bubble tail on the hover card. The card stands off its marker by
|
||
26px and reads as a label, not as a callout pinned to a point — the tail
|
||
hung off the bottom edge as a stray notch. The popup's offset carries the
|
||
gap on its own. */
|
||
.trek-map-popup .mapboxgl-popup-tip,
|
||
.trek-map-popup .maplibregl-popup-tip {
|
||
display: none;
|
||
}
|
||
/* Both engines square off one corner per anchor so the tail meets the card
|
||
flush (.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content and
|
||
its three siblings). Same specificity as the rule above, so which one wins
|
||
comes down to stylesheet order — and with no tail there, a squared corner is
|
||
just a squared corner. `[class]` buys the extra weight to settle it. */
|
||
.trek-map-popup[class] .mapboxgl-popup-content,
|
||
.trek-map-popup[class] .maplibregl-popup-content {
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.atlas-tooltip {
|
||
background: rgba(10, 10, 20, 0.6) !important;
|
||
backdrop-filter: blur(20px) saturate(180%) !important;
|
||
-webkit-backdrop-filter: blur(20px) saturate(180%) !important;
|
||
color: #f1f5f9 !important;
|
||
border: 1px solid rgba(255,255,255,0.1) !important;
|
||
border-radius: 14px !important;
|
||
padding: 10px 14px !important;
|
||
font-size: 12px !important;
|
||
font-family: inherit !important;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.25) !important;
|
||
transition: none !important;
|
||
max-width: min(280px, calc(100vw - 32px)) !important;
|
||
white-space: normal !important;
|
||
word-wrap: break-word !important;
|
||
}
|
||
.atlas-tooltip::before { border-top-color: rgba(10, 10, 20, 0.6) !important; }
|
||
/* Bucket-list marker tooltip only — arbitrary-length user notes need a scroll
|
||
cap. The scroll happens on .atlas-tooltip-scroll-inner, not this element,
|
||
so the outer border-radius clips the scrollbar instead of it poking
|
||
through the rounded corner. overflow-y on the inner div is toggled
|
||
per-instance in useAtlas.ts, not forced here. */
|
||
.atlas-tooltip-scrollable {
|
||
width: min(480px, calc(100vw - 32px)) !important;
|
||
max-width: min(480px, calc(100vw - 32px)) !important;
|
||
overflow: hidden !important;
|
||
pointer-events: auto !important;
|
||
}
|
||
.atlas-tooltip-scroll-inner {
|
||
max-height: min(200px, 40vh);
|
||
overflow-y: hidden;
|
||
/* Leaflet puts touch-action: none on the map container; without widening it
|
||
back here a touch swipe over the note can never scroll it (#2153). */
|
||
touch-action: pan-y;
|
||
}
|
||
.atlas-tooltip-scroll-inner::-webkit-scrollbar { width: 6px; }
|
||
.atlas-tooltip-scroll-inner::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.25); border-radius: 3px; }
|
||
html:not(.dark) .atlas-tooltip-scroll-inner::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.2); }
|
||
html:not(.dark) .atlas-tooltip {
|
||
background: rgba(255, 255, 255, 0.75) !important;
|
||
color: #0f172a !important;
|
||
border: 1px solid rgba(0,0,0,0.08) !important;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.1) !important;
|
||
}
|
||
html:not(.dark) .atlas-tooltip::before { border-top-color: rgba(255, 255, 255, 0.75) !important; }
|
||
.leaflet-tooltip.atlas-tooltip { opacity: 1 !important; }
|
||
.leaflet-tooltip-pane { transition: none !important; }
|
||
.leaflet-fade-anim .leaflet-tooltip { transition: none !important; opacity: 1 !important; }
|
||
|
||
.dark .leaflet-control-zoom a {
|
||
background: rgba(10, 10, 20, 0.7) !important;
|
||
color: #e2e8f0 !important;
|
||
border-color: rgba(255, 255, 255, 0.1) !important;
|
||
backdrop-filter: blur(12px);
|
||
}
|
||
.dark .leaflet-control-zoom a:hover {
|
||
background: rgba(30, 30, 40, 0.8) !important;
|
||
}
|
||
@media (max-width: 767px), (pointer: coarse) and (max-height: 500px) {
|
||
.leaflet-control-zoom { display: none !important; }
|
||
}
|
||
|
||
/* Dark mode overrides for pages using hardcoded slate-* Tailwind classes */
|
||
html.dark .bg-slate-50 { background-color: var(--bg-secondary) !important; }
|
||
html.dark .bg-white { background-color: var(--bg-card) !important; }
|
||
html.dark .bg-slate-100 { background-color: var(--bg-secondary) !important; }
|
||
html.dark .bg-slate-900.text-white { background-color: #e2e8f0 !important; color: #0f172a !important; }
|
||
html.dark .border-slate-200, html.dark .border-slate-300 { border-color: var(--border-primary) !important; }
|
||
html.dark .border-slate-100, html.dark .border-b-slate-100 { border-color: var(--border-secondary) !important; }
|
||
html.dark .text-slate-900 { color: var(--text-primary) !important; }
|
||
html.dark .text-slate-700 { color: var(--text-secondary) !important; }
|
||
html.dark .text-slate-600 { color: var(--text-muted) !important; }
|
||
html.dark .text-slate-500 { color: var(--text-muted) !important; }
|
||
html.dark .text-slate-400 { color: var(--text-faint) !important; }
|
||
html.dark .hover\:bg-slate-50:hover, html.dark .hover\:bg-slate-100:hover { background-color: var(--bg-hover) !important; }
|
||
html.dark .hover\:text-slate-900:hover { color: var(--text-primary) !important; }
|
||
html.dark .hover\:bg-slate-700:hover { background-color: var(--bg-hover) !important; }
|
||
html.dark .divide-slate-100 > :not([hidden]) ~ :not([hidden]) { border-color: var(--border-secondary) !important; }
|
||
html.dark .focus\:ring-slate-400:focus { --tw-ring-color: var(--text-faint) !important; }
|
||
html.dark input[class*="border-slate"], html.dark input[class*="text-slate"] { background: var(--bg-secondary) !important; color: var(--text-primary) !important; border-color: var(--border-primary) !important; }
|
||
html.dark .text-amber-900 { color: #fbbf24 !important; }
|
||
html.dark .text-amber-700 { color: #f59e0b !important; }
|
||
html.dark .bg-amber-50 { background-color: rgba(245,158,11,0.1) !important; }
|
||
html.dark .border-amber-200 { border-color: rgba(245,158,11,0.2) !important; }
|
||
html.dark .disabled\:bg-slate-400:disabled { background-color: var(--text-faint) !important; }
|
||
html.dark button.bg-slate-900 { background-color: #e2e8f0 !important; color: #0f172a !important; opacity: 1 !important; }
|
||
html.dark button.bg-slate-900:hover { background-color: #cbd5e1 !important; }
|
||
html.dark button.bg-slate-900:disabled { background-color: #ffffff !important; color: #000000 !important; opacity: 0.4 !important; }
|
||
html.dark span.bg-slate-900 { background-color: #e2e8f0 !important; color: #0f172a !important; }
|
||
html.dark span.bg-slate-100 { background-color: var(--bg-secondary) !important; color: var(--text-muted) !important; }
|
||
html.dark .border-b { border-bottom-color: var(--border-secondary) !important; }
|
||
/* Gray variants (CategoryManager, BackupPanel) */
|
||
html.dark .bg-gray-50 { background-color: var(--bg-secondary) !important; }
|
||
html.dark .bg-gray-100 { background-color: var(--bg-secondary) !important; }
|
||
html.dark .border-gray-200, html.dark .border-gray-300 { border-color: var(--border-primary) !important; }
|
||
html.dark .border-gray-100 { border-color: var(--border-secondary) !important; }
|
||
html.dark .text-gray-900 { color: var(--text-primary) !important; }
|
||
html.dark .text-gray-700 { color: var(--text-secondary) !important; }
|
||
html.dark .text-gray-600 { color: var(--text-muted) !important; }
|
||
html.dark .text-gray-500 { color: var(--text-muted) !important; }
|
||
html.dark .text-gray-400 { color: var(--text-faint) !important; }
|
||
html.dark .text-gray-300 { color: var(--text-faint) !important; }
|
||
html.dark .hover\:bg-gray-50:hover, html.dark .hover\:bg-gray-200:hover { background-color: var(--bg-hover) !important; }
|
||
html.dark .hover\:border-gray-200:hover, html.dark .hover\:border-gray-400:hover { border-color: var(--border-primary) !important; }
|
||
html.dark input.bg-white, html.dark input[class*="bg-white"] { background: var(--bg-secondary) !important; color: var(--text-primary) !important; }
|
||
html.dark .bg-gray-200 { background-color: var(--border-primary) !important; }
|
||
html.dark .border-gray-300.border-t-slate-600 { border-color: var(--border-primary) !important; border-top-color: var(--text-primary) !important; }
|
||
/* Modal buttons */
|
||
html.dark button[class*="text-slate-600"][class*="border-slate-200"] { color: var(--text-muted) !important; border-color: var(--border-primary) !important; }
|
||
html.dark button[class*="text-slate-600"][class*="border-slate-200"]:hover { background: var(--bg-hover) !important; }
|
||
/* Dashed borders */
|
||
html.dark .border-dashed.border-gray-300 { border-color: var(--border-primary) !important; }
|
||
html.dark .bg-slate-50\/60, html.dark [class*="bg-slate-50/"] { background-color: transparent !important; }
|
||
|
||
/* Reorder buttons: desktop = original style; touch = always visible, larger touch targets.
|
||
Keyed on pointer as well as width — a tablet is a wide touch device with no hover, so
|
||
without this the buttons would never appear there. (Drag reorder itself is no longer off
|
||
on a coarse pointer: #1616 gave tablets the long-press bridge.) */
|
||
.reorder-buttons {
|
||
flex-direction: column;
|
||
opacity: 0;
|
||
/* Hidden means non-interactive. Opacity alone still hit-tests, which left an invisible
|
||
but fully clickable target on every row that silently reordered the itinerary. */
|
||
pointer-events: none;
|
||
}
|
||
.reorder-buttons button {
|
||
background: none !important;
|
||
padding: 1px 2px;
|
||
}
|
||
/* Reveal on row hover (and on keyboard focus, so the buttons are reachable by Tab). */
|
||
.dp-row:hover .reorder-buttons,
|
||
.dp-row:focus-within .reorder-buttons {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
}
|
||
@media (max-width: 767px), (pointer: coarse) {
|
||
.reorder-buttons {
|
||
flex-direction: row !important;
|
||
opacity: 1 !important;
|
||
/* There is no hover on touch, so these are always live — undo the base rule. */
|
||
pointer-events: auto !important;
|
||
align-items: center;
|
||
margin-inline-start: auto;
|
||
}
|
||
.reorder-buttons button {
|
||
background: var(--bg-tertiary) !important;
|
||
border-radius: 6px;
|
||
width: 32px;
|
||
height: 32px;
|
||
padding: 0 !important;
|
||
display: flex !important;
|
||
align-items: center !important;
|
||
justify-content: center !important;
|
||
}
|
||
}
|
||
|
||
/* Packing list rows: on phones, keep the item name readable and move the dense
|
||
per-item controls behind the overflow menu. On a wide screen the row shows
|
||
them itself, so the menu leaves them out; on a phone, with no hover to bring
|
||
it up, the menu button stays in sight. */
|
||
@media (min-width: 640px) {
|
||
.packing-menu-phone-only {
|
||
display: none;
|
||
}
|
||
}
|
||
@media (max-width: 639px) {
|
||
.packing-item-row {
|
||
gap: 6px !important;
|
||
padding: 7px 8px !important;
|
||
}
|
||
.packing-item-row > span {
|
||
flex-basis: auto !important;
|
||
}
|
||
.packing-row-inline-actions,
|
||
.packing-row-badge {
|
||
display: none !important;
|
||
}
|
||
.packing-row-overflow {
|
||
opacity: 1 !important;
|
||
}
|
||
}
|
||
|
||
/* Ladeanimation */
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* ── Press-Feedback + bessere Easings (Emil Kowalski) ─────────── */
|
||
/* Buttons sollen antworten wenn sie gedrückt werden.
|
||
Map-Marker sind ausgenommen: MapLibre und Mapbox geben ihrem Marker-Element
|
||
role="button" und schreiben dessen `transform` bei jedem Frame einer Bewegung
|
||
neu. Mit einem Übergang darauf hinkt die Nadel dem Kartenzug hinterher und
|
||
springt am Ende an ihren Ort zurück, und ein Klick würde den scale(0.97) über
|
||
die Positionierung der Bibliothek legen. */
|
||
button:not(:disabled):not([data-no-press]),
|
||
[role="button"]:not([aria-disabled="true"]):not([data-no-press]):not(.maplibregl-marker):not(.mapboxgl-marker) {
|
||
transition-property: transform, color, background-color, border-color, box-shadow, opacity, filter !important;
|
||
transition-duration: 180ms;
|
||
transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
|
||
}
|
||
button:not(:disabled):not([data-no-press]):active,
|
||
[role="button"]:not([aria-disabled="true"]):not([data-no-press]):not(.maplibregl-marker):not(.mapboxgl-marker):active {
|
||
transform: scale(0.97);
|
||
transition-duration: 80ms;
|
||
}
|
||
|
||
/* Tailwind-Default-Easing durch ease-out-quint ersetzen.
|
||
Eingebaute CSS-Easings sind kraftlos; ease-out-quint hat Punch. */
|
||
.transition,
|
||
.transition-all,
|
||
.transition-colors,
|
||
.transition-opacity,
|
||
.transition-transform,
|
||
.transition-shadow {
|
||
transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
|
||
}
|
||
|
||
/* Input-Focus transitions — border + ring faden weich ein */
|
||
input, textarea, select {
|
||
transition: border-color 150ms cubic-bezier(0.23, 1, 0.32, 1),
|
||
box-shadow 150ms cubic-bezier(0.23, 1, 0.32, 1),
|
||
background-color 150ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
}
|
||
|
||
/* Back-Button Icon-Slide on hover */
|
||
.trek-back-btn .trek-back-icon {
|
||
transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
}
|
||
.trek-back-btn:hover .trek-back-icon {
|
||
transform: translateX(-2px);
|
||
}
|
||
|
||
/* Global focus-visible ring — konsistent überall */
|
||
:focus-visible {
|
||
outline: 2px solid var(--accent);
|
||
outline-offset: 2px;
|
||
border-radius: 4px;
|
||
}
|
||
button:focus-visible, [role="button"]:focus-visible, a:focus-visible {
|
||
outline-offset: 3px;
|
||
}
|
||
input:focus-visible, textarea:focus-visible, select:focus-visible {
|
||
outline: none;
|
||
}
|
||
|
||
/* Theme crossfade — beim Dark/Light switch, Hauptflächen + Text faden ihre Farben.
|
||
Sparingly: nur background-color und color bekommen eine Transition. */
|
||
html.trek-theme-transitioning,
|
||
html.trek-theme-transitioning body,
|
||
html.trek-theme-transitioning *:not(img):not(video):not(canvas):not([class*="trek-skeleton"]):not(.leaflet-layer) {
|
||
transition:
|
||
background-color 320ms cubic-bezier(0.23, 1, 0.32, 1),
|
||
color 320ms cubic-bezier(0.23, 1, 0.32, 1),
|
||
border-color 320ms cubic-bezier(0.23, 1, 0.32, 1),
|
||
fill 320ms cubic-bezier(0.23, 1, 0.32, 1) !important;
|
||
}
|
||
|
||
/* Touch-Geräte: iOS-Tap-Highlight weg (wir haben eigenes Press-Feedback) */
|
||
@media (hover: none) {
|
||
button, [role="button"], a {
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
}
|
||
html, body {
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
|
||
/* Tabular-nums global für Time/Date/Currency/Counter */
|
||
time, .tabular-nums, [data-tabular],
|
||
input[type="number"], input[type="time"], input[type="date"], input[type="datetime-local"] {
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
/* Wenn Element explizit ease-in-out nutzt (z.B. Accordions), nicht überschreiben.
|
||
Tailwind setzt ease-in-out via eigener Klasse — die gewinnt durch letzte Deklaration. */
|
||
|
||
/* Press-Scale für clickbare Divs (Cards, Tiles) — sanfter als Buttons */
|
||
[data-press]:active {
|
||
transform: scale(0.985);
|
||
transition-duration: 80ms;
|
||
}
|
||
|
||
/* ── Popover/Dropdown Enter-Animationen ─────────────────────────
|
||
Emil: Popovers sollen von ihrem Trigger aus scalen, nicht vom Center.
|
||
Start bei scale(0.95) — nichts in der echten Welt poppt aus dem Nichts. */
|
||
@keyframes trek-menu-enter {
|
||
from { opacity: 0; transform: scale(0.95) translateY(-4px); }
|
||
to { opacity: 1; transform: scale(1) translateY(0); }
|
||
}
|
||
@keyframes trek-popover-enter {
|
||
from { opacity: 0; transform: scale(0.96); }
|
||
to { opacity: 1; transform: scale(1); }
|
||
}
|
||
@keyframes trek-modal-enter {
|
||
from { opacity: 0; transform: scale(0.97); }
|
||
to { opacity: 1; transform: scale(1); }
|
||
}
|
||
@keyframes trek-backdrop-enter {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
@keyframes trek-toast-enter {
|
||
from { opacity: 0; transform: translateY(8px) scale(0.96); }
|
||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
@keyframes trek-progress-fill {
|
||
from { width: 0%; }
|
||
to { width: var(--trek-progress-to, 0%); }
|
||
}
|
||
|
||
/* Pie-Chart Reveal — rotate + fade-in, gibt dem Kreisdiagramm ein "Draw"-Gefühl */
|
||
@keyframes trek-pie-reveal {
|
||
from { opacity: 0; transform: rotate(-90deg) scale(0.85); }
|
||
to { opacity: 1; transform: rotate(0deg) scale(1); }
|
||
}
|
||
.trek-pie-reveal {
|
||
animation: trek-pie-reveal 900ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||
transform-origin: center;
|
||
will-change: transform, opacity;
|
||
}
|
||
|
||
/* Bar-Chart Reveal — horizontaler Fill von links */
|
||
@keyframes trek-bar-fill {
|
||
from { transform: scaleX(0); }
|
||
to { transform: scaleX(1); }
|
||
}
|
||
.trek-bar-fill {
|
||
animation: trek-bar-fill 700ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||
transform-origin: left center;
|
||
will-change: transform;
|
||
}
|
||
|
||
/* Page-Transition — subtiler Fade-Up beim Mount */
|
||
@keyframes trek-page-enter {
|
||
from { opacity: 0; transform: translateY(6px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.trek-page-enter {
|
||
animation: trek-page-enter 220ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||
}
|
||
|
||
/* Skeleton shimmer — ein fließender Gradient-Strip überquert den Platzhalter */
|
||
@keyframes trek-shimmer {
|
||
from { background-position: -200% 0; }
|
||
to { background-position: 200% 0; }
|
||
}
|
||
.trek-skeleton {
|
||
background: linear-gradient(
|
||
90deg,
|
||
var(--bg-tertiary) 0%,
|
||
var(--bg-hover) 50%,
|
||
var(--bg-tertiary) 100%
|
||
);
|
||
background-size: 200% 100%;
|
||
animation: trek-shimmer 1.6s linear infinite;
|
||
border-radius: 8px;
|
||
color: transparent;
|
||
user-select: none;
|
||
}
|
||
.dark .trek-skeleton {
|
||
background: linear-gradient(
|
||
90deg,
|
||
rgba(255,255,255,0.04) 0%,
|
||
rgba(255,255,255,0.08) 50%,
|
||
rgba(255,255,255,0.04) 100%
|
||
);
|
||
background-size: 200% 100%;
|
||
}
|
||
.trek-menu-enter {
|
||
animation: trek-menu-enter 200ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
transform-origin: top right;
|
||
will-change: transform, opacity;
|
||
}
|
||
.trek-menu-enter-left {
|
||
animation: trek-menu-enter 200ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
transform-origin: top left;
|
||
will-change: transform, opacity;
|
||
}
|
||
.trek-popover-enter {
|
||
animation: trek-popover-enter 180ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
will-change: transform, opacity;
|
||
}
|
||
.trek-modal-enter {
|
||
animation: trek-modal-enter 220ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
will-change: transform, opacity;
|
||
}
|
||
|
||
/* Dokumentenabgleich: die aktive Bahn laeuft, WAEHREND ein Lauf laeuft.
|
||
Bewegung als Antwort auf eine Handlung, nicht als Dekoration: sie hoert auf,
|
||
sobald der Lauf durch ist, und sagt in der Zwischenzeit, in welche Richtung
|
||
gerade etwas unterwegs ist. */
|
||
@keyframes trek-docsync-lane {
|
||
from { background-position: 0 0; }
|
||
to { background-position: 16px 0; }
|
||
}
|
||
.trek-docsync-lane-run {
|
||
background-image: repeating-linear-gradient(
|
||
90deg,
|
||
currentColor 0 5px,
|
||
transparent 5px 16px
|
||
);
|
||
animation: trek-docsync-lane 700ms linear infinite;
|
||
}
|
||
.trek-docsync-lane-run-rev {
|
||
animation-direction: reverse;
|
||
}
|
||
|
||
/* Die abgeschaltete Richtung bekommt eine Schraffur, dasselbe Zeichen, das eine
|
||
gesperrte Flaeche ueberall traegt. Es liest sich als "hier geht nichts durch",
|
||
ohne dass die Bahn verschwindet und die Zeile zusammenklappt. */
|
||
.trek-docsync-lane-off {
|
||
background-image: repeating-linear-gradient(
|
||
-45deg,
|
||
var(--border-primary) 0 5px,
|
||
transparent 5px 20px
|
||
);
|
||
}
|
||
|
||
/* Dieselbe Schraffur im Telefon-Schema: die mobilen Flaechen kennen die
|
||
Desktop-Randfarbe nicht, und eine Fremdfarbe faellt dort sofort auf. */
|
||
.trek-docsync-lane-off-m {
|
||
background-image: repeating-linear-gradient(
|
||
-45deg,
|
||
var(--m-rowbr) 0 5px,
|
||
transparent 5px 20px
|
||
);
|
||
}
|
||
|
||
/* Mobile-Drawer-Feel — Modal slidet von unten rein, wird unten am Screen angedockt */
|
||
@keyframes trek-drawer-enter {
|
||
from { opacity: 0; transform: translateY(100%); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
@media (max-width: 639px) {
|
||
.trek-modal-enter {
|
||
animation: trek-drawer-enter 320ms cubic-bezier(0.32, 0.72, 0, 1);
|
||
border-end-start-radius: 0 !important;
|
||
border-end-end-radius: 0 !important;
|
||
margin-top: auto !important;
|
||
align-self: flex-end;
|
||
}
|
||
}
|
||
.trek-backdrop-enter {
|
||
animation: trek-backdrop-enter 180ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
}
|
||
.trek-toast-enter {
|
||
animation: trek-toast-enter 260ms cubic-bezier(0.23, 1, 0.32, 1);
|
||
will-change: transform, opacity;
|
||
}
|
||
|
||
/* Roadtrip: die Zzz am Tageswechsel im Streckenstrang. Drei Buchstaben auf einer
|
||
Grundlinie, jeder ein Drittel des Laufs nach dem vorigen, damit immer einer
|
||
unterwegs ist. Rein dekorativ — das Wort daneben sagt schon, was das Band ist. */
|
||
@keyframes trek-doze {
|
||
0% { opacity: 0; transform: translate(0, 3px) scale(0.7); }
|
||
22% { opacity: 0.9; }
|
||
100% { opacity: 0; transform: translate(6px, -10px) scale(1.15); }
|
||
}
|
||
.trek-doze > * {
|
||
animation: trek-doze 3s ease-out infinite;
|
||
}
|
||
.trek-doze > *:nth-child(2) { animation-delay: 1s; }
|
||
.trek-doze > *:nth-child(3) { animation-delay: 2s; }
|
||
|
||
/* Roadtrip: der Block, den eine Nachtfahrt auf dem Folgetag hinterlaesst. Er faellt
|
||
von oben in die Karte, weil er von der Karte darueber kommt — kurz genug, dass ein
|
||
Neuberechnen der Ankunftszeiten nicht als Zappeln liest. */
|
||
@keyframes trek-spill-in {
|
||
from { opacity: 0; transform: translateY(-6px) scale(0.985); }
|
||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
.trek-spill-in {
|
||
animation: trek-spill-in 320ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||
}
|
||
|
||
/* Roadtrip: die Reserveleuchte am Band, an dem der Tank leerlaeuft. Das Gegenstueck
|
||
zu den Zzz am Tageswechsel — dort schlaeft der Tag ein, hier geht die Lampe im
|
||
Armaturenbrett an. Langsam an, kurz aus: eine Warnleuchte blinkt nicht hektisch,
|
||
sie glimmt. */
|
||
@keyframes trek-lowfuel {
|
||
0%, 62%, 100% { opacity: 1; }
|
||
74% { opacity: 0.28; }
|
||
86% { opacity: 1; }
|
||
}
|
||
.trek-lowfuel {
|
||
animation: trek-lowfuel 2.6s ease-in-out infinite;
|
||
}
|
||
|
||
/* Stagger-Helpers für Listen — Enter-Animation mit Offset */
|
||
@keyframes trek-fade-up {
|
||
from { opacity: 0; transform: translateY(6px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.trek-stagger > * {
|
||
animation: trek-fade-up 280ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||
}
|
||
.trek-stagger > *:nth-child(1) { animation-delay: 0ms; }
|
||
.trek-stagger > *:nth-child(2) { animation-delay: 40ms; }
|
||
.trek-stagger > *:nth-child(3) { animation-delay: 80ms; }
|
||
.trek-stagger > *:nth-child(4) { animation-delay: 120ms; }
|
||
.trek-stagger > *:nth-child(5) { animation-delay: 160ms; }
|
||
.trek-stagger > *:nth-child(6) { animation-delay: 200ms; }
|
||
.trek-stagger > *:nth-child(7) { animation-delay: 240ms; }
|
||
.trek-stagger > *:nth-child(8) { animation-delay: 280ms; }
|
||
.trek-stagger > *:nth-child(n+9) { animation-delay: 320ms; }
|
||
|
||
/* Reduced motion — Emil's Accessibility-Regel: fewer and gentler, not zero */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.trek-menu-enter, .trek-menu-enter-left, .trek-popover-enter,
|
||
.trek-modal-enter, .trek-toast-enter, .trek-stagger > *, .trek-spill-in {
|
||
animation: trek-backdrop-enter 120ms ease-out;
|
||
}
|
||
/* Die Reserveleuchte steht still statt zu glimmen — sie bleibt an, was die Aussage
|
||
ohnehin ist. */
|
||
.trek-lowfuel {
|
||
animation: none;
|
||
}
|
||
/* Die Bahn bleibt sichtbar, sie laeuft nur nicht mehr. Die Aussage
|
||
"hier ist gerade etwas unterwegs" traegt auch im Stillstand. */
|
||
.trek-docsync-lane-run {
|
||
animation: none;
|
||
}
|
||
.trek-skeleton {
|
||
animation: none;
|
||
background: var(--bg-tertiary);
|
||
}
|
||
/* Die Zzz stehen still statt zu treiben — der Tageswechsel bleibt lesbar. */
|
||
.trek-doze > * {
|
||
animation: none;
|
||
opacity: 0.6;
|
||
transform: none;
|
||
}
|
||
button:not(:disabled):not([data-no-press]):active,
|
||
[role="button"]:not([aria-disabled="true"]):not([data-no-press]):active,
|
||
[data-press]:active {
|
||
transform: none;
|
||
}
|
||
/* Parallax & lift disablen */
|
||
.group:hover img,
|
||
.group:hover .cover-img { transform: none !important; }
|
||
*:hover { translate: none !important; }
|
||
}
|
||
|
||
/* ── Design tokens ─────────────────────────────── */
|
||
:root {
|
||
/* Easing curves — stärker als die CSS-Defaults, siehe easing.dev */
|
||
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
|
||
--ease-in-out-quint: cubic-bezier(0.77, 0, 0.175, 1);
|
||
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
|
||
|
||
--safe-top: env(safe-area-inset-top, 0px);
|
||
--nav-h: 0px;
|
||
--bottom-nav-h: 0px;
|
||
|
||
/* Layer scale. Anything fixed or sticky picks a step from here instead of
|
||
inventing a number, so two overlays can no longer land in the wrong order
|
||
by accident. The steps are spaced so today's hardcoded z-indexes keep the
|
||
position they already have; each of those moves onto its token whenever
|
||
its file gets touched for other reasons. */
|
||
--z-bar: 60; /* BottomNav, page-bound sticky bars */
|
||
--z-nav: 200; /* the desktop navbar */
|
||
--z-modal: 10000; /* shared/Modal, ConfirmDialog */
|
||
--z-overlay: 99990; /* page-owned full-screen overlays */
|
||
--z-notice: 99998; /* app-wide system and release notices */
|
||
--z-toast: 100000; /* toasts, tooltips, context menus */
|
||
|
||
--font-system: 'Poppins', -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', system-ui, sans-serif;
|
||
/* Secondary "subtext"/caption tier renders in Geist; primary text + headings stay Poppins. */
|
||
--font-subtext: 'Geist Sans', 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
||
--sp-1: 4px;
|
||
--sp-2: 8px;
|
||
--sp-3: 12px;
|
||
--sp-4: 16px;
|
||
--sp-6: 24px;
|
||
--radius-sm: 8px;
|
||
--radius-md: 12px;
|
||
--radius-lg: 16px;
|
||
--radius-xl: 20px;
|
||
--shadow-card: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
|
||
--shadow-elevated: 0 4px 16px rgba(0,0,0,0.1);
|
||
|
||
/* Theme colors */
|
||
--bg-primary: #ffffff;
|
||
--bg-secondary: #f8fafc;
|
||
--bg-tertiary: #f1f5f9;
|
||
--bg-elevated: rgba(250,250,250,0.82);
|
||
--bg-card: #ffffff;
|
||
--bg-input: #ffffff;
|
||
--bg-hover: rgba(0,0,0,0.03);
|
||
--bg-selected: #e2e8f0;
|
||
--text-primary: #111827;
|
||
--text-secondary: #374151;
|
||
--text-muted: #6b7280;
|
||
--text-faint: #9ca3af;
|
||
--border-primary: #e5e7eb;
|
||
--border-secondary: #f3f4f6;
|
||
--border-faint: rgba(0,0,0,0.06);
|
||
--accent: #111827;
|
||
--accent-text: #ffffff;
|
||
--sidebar-bg: rgba(250,250,250,0.82);
|
||
--sidebar-shadow: 0 4px 32px rgba(0,0,0,0.10), 0 0 0 1px rgba(0,0,0,0.06);
|
||
--tooltip-bg: rgba(255,255,255,0.96);
|
||
--scrollbar-track: #f1f5f9;
|
||
--scrollbar-thumb: #d1d5db;
|
||
--scrollbar-hover: #9ca3af;
|
||
|
||
/* ── Extended semantic tokens (appearance platform) ──────────────
|
||
Accent variants + status/shadow/overlay/inverse + per-tier type scales.
|
||
Additive: nothing consumes these until components migrate to them, so
|
||
defining them is visually inert. Colored schemes override --accent* via
|
||
[data-scheme]; status/shadow/inverse stay scheme-independent. */
|
||
--accent-on: var(--accent); /* accent as text/icon/border on a surface */
|
||
--accent-hover: #1f2937; /* hover state of an accent fill */
|
||
--accent-subtle: #f1f5f9; /* faint accent-tinted surface (chips, selected) */
|
||
|
||
/* Strength of a dayTintProvider plugin's colour in each region of a day card.
|
||
Per-theme because the tones are fixed hexes: the default tone #4F46E5 is itself a
|
||
dark colour, so a light theme's alpha makes it vanish against the dark sidebar,
|
||
while warn #f59e0b at the dark alpha would outshine the selected day. Per-region
|
||
because area and text density differ — the badge is a small bold pill, the
|
||
activity list is a large surface behind dense rows. Tuned so every region reads
|
||
above the hover step and below the selected one. */
|
||
--day-tint-badge: 16%;
|
||
--day-tint-header: 8%;
|
||
--day-tint-header-hover: 14%;
|
||
--day-tint-activity: 6%;
|
||
|
||
/* The OKLCH lightness band a plugin's OWN colour is clamped into before it is mixed,
|
||
so "any hex" cannot mean "a day nobody can read". The band brackets the four tones
|
||
above (L 0.51–0.77) rather than tightening around them: an ordinary choice passes
|
||
through untouched, and a hex equal to a tone renders exactly like that tone. Only
|
||
the extremes move — here, a near-white that would otherwise wash out against the
|
||
sidebar's own near-white surfaces.
|
||
|
||
This bounds a colour, it does not boost one: hue and chroma are never touched, so a
|
||
near-white GREY still lands as a faint tint. That is the intended trade — the
|
||
failure this must prevent is an unreadable card, not an understated one. */
|
||
--day-tint-l-min: 0.40;
|
||
--day-tint-l-max: 0.78;
|
||
|
||
--fs-scale-title: 1;
|
||
--fs-scale-subtitle: 1;
|
||
--fs-scale-body: 1;
|
||
--fs-scale-caption: 1;
|
||
|
||
--success: #16a34a; --success-soft: #dcfce7;
|
||
--danger: #dc2626; --danger-soft: #fef2f2;
|
||
--warning: #d97706; --warning-soft: #fffbeb;
|
||
--info: #2563eb; --info-soft: #eff6ff;
|
||
|
||
--bg-inverse: #111827; --text-inverse: #ffffff;
|
||
--overlay: rgba(0, 0, 0, 0.5);
|
||
|
||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||
--shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.12);
|
||
--shadow-modal: 0 16px 48px rgba(0, 0, 0, 0.20);
|
||
--shadow-dropdown: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||
--shadow-popover: 0 8px 32px rgba(0, 0, 0, 0.18);
|
||
|
||
/* Journey design tokens */
|
||
--journal-bg: #FAFAFA;
|
||
--journal-card: #FFFFFF;
|
||
--journal-border: #E4E4E7;
|
||
--journal-accent: #6366F1;
|
||
--journal-text: #09090B;
|
||
--journal-muted: #71717A;
|
||
--journal-faint: #A1A1AA;
|
||
--mood-amazing: #E8654A;
|
||
--mood-good: #EF9F27;
|
||
--mood-neutral: #94928C;
|
||
--mood-tired: #6B9BD2;
|
||
--mood-rough: #9B8EC4;
|
||
|
||
/* ── Vacay "liquid glass" facelift tokens (desktop) ──────────────
|
||
Prefixed --vg-*; only the Vacay surface consumes them. Values are
|
||
the light palette; .dark overrides below. Kept global (not scoped)
|
||
so the tablet sidebar drawer — which portals to <body> — still
|
||
resolves them in dark mode. */
|
||
--vg-ink: oklch(0.22 0.012 65);
|
||
--vg-ink2: oklch(0.42 0.012 65);
|
||
--vg-ink3: oklch(0.62 0.01 65);
|
||
--vg-bg: #f8fafc;
|
||
--vg-surf: #ffffff;
|
||
--vg-surf2: oklch(0.968 0.003 247);
|
||
--vg-line: oklch(0.92 0.008 70);
|
||
--vg-line2: oklch(0.88 0.01 70);
|
||
--vg-glass-bg: linear-gradient(135deg, oklch(1 0 0 / .72) 0%, oklch(0.99 0.006 75 / .5) 100%);
|
||
--vg-glass-br: oklch(0.88 0.008 70 / .7);
|
||
--vg-glass-sh: 0 1px 2px oklch(0.4 0.02 60 / .05), 0 12px 32px -14px oklch(0.3 0.02 60 / .2);
|
||
--vg-hi: inset 0 1px 0 oklch(1 0 0 / .8);
|
||
}
|
||
|
||
@media (max-width: 767px), (pointer: coarse) and (max-height: 500px) {
|
||
:root { --bottom-nav-h: calc(84px + env(safe-area-inset-bottom, 0px)); }
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
:root { --nav-h: calc(64px + env(safe-area-inset-top, 0px)); }
|
||
}
|
||
|
||
.dark {
|
||
--bg-primary: #121215;
|
||
--bg-secondary: #1a1a1e;
|
||
--bg-tertiary: #1c1c21;
|
||
--bg-elevated: rgba(19,19,22,0.82);
|
||
--bg-card: #131316;
|
||
--bg-input: #1c1c21;
|
||
--bg-hover: rgba(255,255,255,0.06);
|
||
--bg-selected: rgba(255,255,255,0.1);
|
||
--text-primary: #f4f4f5;
|
||
--text-secondary: #d4d4d8;
|
||
--text-muted: #a1a1aa;
|
||
--text-faint: #71717a;
|
||
--border-primary: #27272a;
|
||
--border-secondary: #1c1c21;
|
||
--border-faint: rgba(255,255,255,0.07);
|
||
--accent: #e4e4e7;
|
||
--accent-text: #09090b;
|
||
--sidebar-bg: rgba(19,19,22,0.82);
|
||
--sidebar-shadow: 0 4px 32px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.05);
|
||
--tooltip-bg: rgba(19,19,22,0.96);
|
||
--scrollbar-track: #131316;
|
||
--scrollbar-thumb: #3f3f46;
|
||
--scrollbar-hover: #52525b;
|
||
|
||
/* Extended semantic tokens (dark) */
|
||
--accent-on: var(--accent);
|
||
--accent-hover: #d4d4d8;
|
||
--accent-subtle: rgba(255, 255, 255, 0.08);
|
||
|
||
--day-tint-badge: 24%;
|
||
--day-tint-header: 13%;
|
||
--day-tint-header-hover: 20%;
|
||
--day-tint-activity: 10%;
|
||
|
||
/* Shifted up, and the reason the band is per theme at all: here the failure is a
|
||
near-BLACK tint disappearing into the dark sidebar, while a pale one reads fine.
|
||
Inherited by the mobile chips too — mobile.css sets only their alpha. */
|
||
--day-tint-l-min: 0.48;
|
||
--day-tint-l-max: 0.88;
|
||
|
||
--success: #22c55e; --success-soft: rgba(34, 197, 94, 0.15);
|
||
--danger: #ef4444; --danger-soft: rgba(239, 68, 68, 0.15);
|
||
--warning: #f59e0b; --warning-soft: rgba(245, 158, 11, 0.15);
|
||
--info: #3b82f6; --info-soft: rgba(59, 130, 246, 0.15);
|
||
|
||
--bg-inverse: #e4e4e7; --text-inverse: #09090b;
|
||
--overlay: rgba(0, 0, 0, 0.6);
|
||
|
||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||
--shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.5);
|
||
--shadow-modal: 0 16px 48px rgba(0, 0, 0, 0.6);
|
||
--shadow-dropdown: 0 8px 24px rgba(0, 0, 0, 0.5);
|
||
--shadow-popover: 0 8px 32px rgba(0, 0, 0, 0.55);
|
||
|
||
/* Journey design tokens (dark) */
|
||
--journal-bg: #09090B;
|
||
--journal-card: #18181B;
|
||
--journal-border: #27272A;
|
||
--journal-accent: #818CF8;
|
||
--journal-text: #FAFAFA;
|
||
--journal-muted: #A1A1AA;
|
||
--journal-faint: #52525B;
|
||
--mood-amazing: #f28a6e;
|
||
--mood-good: #f5b84d;
|
||
--mood-neutral: #9a9a94;
|
||
--mood-tired: #6db3f0;
|
||
--mood-rough: #a9a3f0;
|
||
|
||
/* Vacay "liquid glass" facelift tokens (dark) */
|
||
--vg-ink: oklch(0.96 0 0);
|
||
--vg-ink2: oklch(0.78 0 0);
|
||
--vg-ink3: oklch(0.6 0 0);
|
||
--vg-bg: oklch(0.17 0 0);
|
||
--vg-surf: oklch(0.225 0 0);
|
||
--vg-surf2: oklch(0.255 0 0);
|
||
--vg-line: oklch(0.32 0 0);
|
||
--vg-line2: oklch(0.38 0 0);
|
||
--vg-glass-bg: linear-gradient(135deg, oklch(0.31 0 0 / .58) 0%, oklch(0.25 0 0 / .42) 100%);
|
||
--vg-glass-br: oklch(1 0 0 / .1);
|
||
--vg-glass-sh: 0 1px 2px oklch(0 0 0 / .3), 0 12px 32px -14px oklch(0 0 0 / .55);
|
||
--vg-hi: inset 0 1px 0 oklch(1 0 0 / .09);
|
||
}
|
||
|
||
/* Vacay glass card + button helpers (consume --vg-* above). */
|
||
.vg-card {
|
||
background: var(--vg-glass-bg);
|
||
border: 1px solid var(--vg-glass-br);
|
||
box-shadow: var(--vg-glass-sh), var(--vg-hi);
|
||
-webkit-backdrop-filter: blur(22px) saturate(1.7);
|
||
backdrop-filter: blur(22px) saturate(1.7);
|
||
}
|
||
|
||
/* ── Color schemes (data-scheme) ──────────────────────────────────
|
||
Each non-default scheme overrides the accent family (High-Contrast also
|
||
raises neutral text/border contrast and solidifies surfaces). Light and dark
|
||
are tuned independently. The 'default' scheme sets NO attribute, so the
|
||
monochrome accent above is untouched — existing users see no change. */
|
||
|
||
:root[data-scheme="indigo"] {
|
||
--accent: #4f46e5; --accent-text: #ffffff; --accent-on: #4338ca;
|
||
--accent-hover: #4338ca; --accent-subtle: #eef2ff;
|
||
}
|
||
.dark[data-scheme="indigo"] {
|
||
--accent: #6366f1; --accent-text: #ffffff; --accent-on: #a5b4fc;
|
||
--accent-hover: #4f46e5; --accent-subtle: rgba(99, 102, 241, 0.18);
|
||
}
|
||
|
||
:root[data-scheme="teal"] {
|
||
--accent: #0d9488; --accent-text: #ffffff; --accent-on: #0f766e;
|
||
--accent-hover: #0f766e; --accent-subtle: #f0fdfa;
|
||
}
|
||
.dark[data-scheme="teal"] {
|
||
--accent: #14b8a6; --accent-text: #04211f; --accent-on: #5eead4;
|
||
--accent-hover: #0d9488; --accent-subtle: rgba(20, 184, 166, 0.18);
|
||
}
|
||
|
||
:root[data-scheme="rose"] {
|
||
--accent: #e11d48; --accent-text: #ffffff; --accent-on: #be123c;
|
||
--accent-hover: #be123c; --accent-subtle: #fff1f2;
|
||
}
|
||
.dark[data-scheme="rose"] {
|
||
--accent: #f43f5e; --accent-text: #ffffff; --accent-on: #fda4af;
|
||
--accent-hover: #e11d48; --accent-subtle: rgba(244, 63, 94, 0.18);
|
||
}
|
||
|
||
:root[data-scheme="amber"] {
|
||
--accent: #d97706; --accent-text: #ffffff; --accent-on: #b45309;
|
||
--accent-hover: #b45309; --accent-subtle: #fffbeb;
|
||
}
|
||
.dark[data-scheme="amber"] {
|
||
--accent: #f59e0b; --accent-text: #1c1917; --accent-on: #fcd34d;
|
||
--accent-hover: #d97706; --accent-subtle: rgba(245, 158, 11, 0.18);
|
||
}
|
||
|
||
:root[data-scheme="violet"] {
|
||
--accent: #7c3aed; --accent-text: #ffffff; --accent-on: #6d28d9;
|
||
--accent-hover: #6d28d9; --accent-subtle: #f5f3ff;
|
||
}
|
||
.dark[data-scheme="violet"] {
|
||
--accent: #8b5cf6; --accent-text: #ffffff; --accent-on: #c4b5fd;
|
||
--accent-hover: #7c3aed; --accent-subtle: rgba(139, 92, 246, 0.18);
|
||
}
|
||
|
||
/* High-Contrast — raises neutral text/border contrast and solidifies surfaces,
|
||
directly answering #951 / #1025 (grey-on-grey, transparency hurting legibility). */
|
||
:root[data-scheme="highContrast"] {
|
||
--accent: #1d4ed8; --accent-text: #ffffff; --accent-on: #1d4ed8;
|
||
--accent-hover: #1e40af; --accent-subtle: #dbeafe;
|
||
--text-primary: #000000; --text-secondary: #1f2937;
|
||
--text-muted: #374151; --text-faint: #4b5563;
|
||
--border-primary: #475569; --border-secondary: #94a3b8;
|
||
--border-faint: rgba(0, 0, 0, 0.35);
|
||
--bg-elevated: #ffffff; --bg-hover: #e2e8f0; --bg-selected: #cbd5e1;
|
||
--sidebar-bg: #ffffff; --tooltip-bg: #ffffff;
|
||
}
|
||
.dark[data-scheme="highContrast"] {
|
||
--accent: #60a5fa; --accent-text: #06182f; --accent-on: #93c5fd;
|
||
--accent-hover: #3b82f6; --accent-subtle: rgba(96, 165, 250, 0.2);
|
||
--text-primary: #ffffff; --text-secondary: #f1f5f9;
|
||
--text-muted: #e2e8f0; --text-faint: #cbd5e1;
|
||
--border-primary: #64748b; --border-secondary: #475569;
|
||
--border-faint: rgba(255, 255, 255, 0.35);
|
||
--bg-elevated: #131316; --bg-hover: #2a2a30; --bg-selected: #3a3a42;
|
||
--sidebar-bg: #131316; --tooltip-bg: #131316;
|
||
}
|
||
|
||
/* Custom — the user's picked accent (applyAppearance writes the inline vars;
|
||
color-mix derives hover/subtle so a single picked color yields a full set). */
|
||
:root[data-scheme="custom"] {
|
||
--accent: var(--accent-custom-light, var(--accent));
|
||
--accent-text: var(--accent-custom-text-light, #ffffff);
|
||
--accent-on: var(--accent-custom-light, var(--accent));
|
||
--accent-hover: color-mix(in srgb, var(--accent-custom-light, #4f46e5) 86%, #000);
|
||
--accent-subtle: color-mix(in srgb, var(--accent-custom-light, #4f46e5) 12%, transparent);
|
||
}
|
||
.dark[data-scheme="custom"] {
|
||
--accent: var(--accent-custom-dark, var(--accent));
|
||
--accent-text: var(--accent-custom-text-dark, #ffffff);
|
||
--accent-on: var(--accent-custom-dark, var(--accent));
|
||
--accent-hover: color-mix(in srgb, var(--accent-custom-dark, #6366f1) 84%, #fff);
|
||
--accent-subtle: color-mix(in srgb, var(--accent-custom-dark, #6366f1) 20%, transparent);
|
||
}
|
||
|
||
/* ── Legacy accent bridge (gated) ─────────────────────────────────
|
||
Under a NON-default scheme ONLY, redirect the hardcoded "primary/black"
|
||
action surfaces (and raw indigo accents) to the chosen accent so un-migrated
|
||
components follow the scheme without editing ~50 files. These selectors carry
|
||
higher specificity than the legacy dark shim, so they win under a scheme; on
|
||
the default scheme nothing here matches (zero change for existing users).
|
||
TEMPORARY scaffold — the Phase-8 codemod migrates these to bg-accent and this
|
||
block shrinks file-by-file until it is deleted. */
|
||
html[data-scheme]:not([data-scheme="default"]) :is(.bg-slate-900, .bg-gray-900, .bg-zinc-900, .bg-neutral-900, .bg-black) {
|
||
background-color: var(--accent) !important;
|
||
color: var(--accent-text) !important;
|
||
}
|
||
html[data-scheme]:not([data-scheme="default"]) :is(.bg-slate-900, .bg-gray-900, .bg-zinc-900, .bg-neutral-900, .bg-black):hover {
|
||
background-color: var(--accent-hover) !important;
|
||
}
|
||
html[data-scheme]:not([data-scheme="default"])
|
||
:is(.hover\:bg-slate-900, .hover\:bg-slate-800, .hover\:bg-slate-700, .hover\:bg-gray-900, .hover\:bg-gray-800, .hover\:bg-black):hover {
|
||
background-color: var(--accent-hover) !important;
|
||
color: var(--accent-text) !important;
|
||
}
|
||
html[data-scheme]:not([data-scheme="default"]) :is(.bg-indigo-500, .bg-indigo-600, .bg-indigo-700) {
|
||
background-color: var(--accent) !important;
|
||
color: var(--accent-text) !important;
|
||
}
|
||
html[data-scheme]:not([data-scheme="default"]) :is(.text-indigo-400, .text-indigo-500, .text-indigo-600, .text-indigo-700) {
|
||
color: var(--accent-on) !important;
|
||
}
|
||
html[data-scheme]:not([data-scheme="default"])
|
||
:is(.border-indigo-500, .border-indigo-600, .ring-indigo-500, .ring-indigo-600) {
|
||
border-color: var(--accent) !important;
|
||
}
|
||
|
||
/* ── Transparency off (data-no-transparency) ──────────────────────
|
||
Solidify the alpha-bearing surface tokens, flatten the dashboard liquid-glass,
|
||
and disable backdrop blur app-wide so content over the map is fully opaque
|
||
(#1025). Inert unless the attribute is set. Intentional dimming scrims (modal
|
||
backdrops) stay translucent on purpose — forcing them opaque blacks out the
|
||
screen. */
|
||
html[data-no-transparency] {
|
||
--bg-elevated: #fafafa;
|
||
--bg-hover: #f1f3f5;
|
||
--bg-selected: #e2e8f0;
|
||
--border-faint: #e5e7eb;
|
||
--sidebar-bg: #ffffff;
|
||
--tooltip-bg: #ffffff;
|
||
}
|
||
html.dark[data-no-transparency] {
|
||
--bg-elevated: #131316;
|
||
--bg-hover: #26262c;
|
||
--bg-selected: #2f2f37;
|
||
--border-faint: #27272a;
|
||
--sidebar-bg: #131316;
|
||
--tooltip-bg: #131316;
|
||
}
|
||
html[data-no-transparency] .trek-dash {
|
||
--glass-bg: var(--surface, var(--bg-card));
|
||
--glass-border: var(--line, var(--border-primary));
|
||
--glass-blur: none;
|
||
--glass-highlight: none;
|
||
}
|
||
/* The boarding-pass hero ticket uses its own translucent gradient (not the glass
|
||
tokens) — force it solid. */
|
||
html[data-no-transparency] .trek-dash .hero-pass {
|
||
background: var(--surface, var(--bg-card)) !important;
|
||
}
|
||
html[data-no-transparency] .trek-dash .hero-pass::before { display: none !important; }
|
||
html[data-no-transparency] *,
|
||
html[data-no-transparency] *::before,
|
||
html[data-no-transparency] *::after {
|
||
backdrop-filter: none !important;
|
||
-webkit-backdrop-filter: none !important;
|
||
}
|
||
/* Class-based translucent surfaces (Atlas tooltip, Leaflet controls, GL popups)
|
||
forced solid. Inline rgba() is handled per-component (it can't be reached by CSS). */
|
||
html[data-no-transparency] .atlas-tooltip {
|
||
background: var(--bg-card) !important;
|
||
color: var(--text-primary) !important;
|
||
border-color: var(--border-primary) !important;
|
||
}
|
||
html[data-no-transparency] .atlas-tooltip::before { border-top-color: var(--bg-card) !important; }
|
||
html[data-no-transparency] .leaflet-control-zoom a { background: var(--bg-card) !important; }
|
||
html[data-no-transparency] .map-tooltip,
|
||
html[data-no-transparency] .leaflet-tooltip.map-tooltip { background: var(--bg-card) !important; }
|
||
html[data-no-transparency] .trek-map-popup .mapboxgl-popup-content,
|
||
html[data-no-transparency] .trek-map-popup .maplibregl-popup-content { background: var(--bg-card) !important; }
|
||
|
||
/* ── Compact density ──────────────────────────────────────────────
|
||
Tightens the spacing scale; reach grows as components adopt the tokens. */
|
||
html[data-density="compact"] {
|
||
--sp-1: 3px;
|
||
--sp-2: 6px;
|
||
--sp-3: 9px;
|
||
--sp-4: 12px;
|
||
--sp-6: 18px;
|
||
}
|
||
html[data-density="compact"] .form-input { padding: 5px 11px; }
|
||
|
||
body {
|
||
font-family: var(--font-system);
|
||
background-color: var(--bg-primary);
|
||
color: var(--text-primary);
|
||
transition: background-color 0.2s, color 0.2s;
|
||
}
|
||
|
||
/* Subtext tier in Geist. The faint text token is TREK's caption/secondary tier;
|
||
a direct rule on the element beats the Poppins inherited from wrapper styles,
|
||
giving the design's "Geist text · Poppins numbers" hierarchy. */
|
||
.text-content-faint { font-family: var(--font-subtext); }
|
||
|
||
/* ── Marker cluster custom styling ────────────── */
|
||
.marker-cluster-wrapper {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
}
|
||
|
||
/* A marker being dragged onto a day (#891): it stays under the cursor as the
|
||
browser's drag image, so what is left behind is dimmed to show it is in
|
||
flight rather than still sitting there. */
|
||
.marker-dragging {
|
||
opacity: 0.4;
|
||
}
|
||
|
||
.marker-cluster-custom {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
background: #111827;
|
||
border: 2.5px solid rgba(255, 255, 255, 0.9);
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25), 0 0 0 2px rgba(17, 24, 39, 0.15);
|
||
cursor: pointer;
|
||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||
}
|
||
|
||
.marker-cluster-custom:hover {
|
||
transform: scale(1.1);
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35), 0 0 0 3px rgba(17, 24, 39, 0.2);
|
||
}
|
||
|
||
.marker-cluster-custom span {
|
||
font-family:var(--font-system);
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* Hide default markercluster styles we don't need */
|
||
.marker-cluster-small,
|
||
.marker-cluster-medium,
|
||
.marker-cluster-large {
|
||
background: transparent !important;
|
||
}
|
||
.marker-cluster-small div,
|
||
.marker-cluster-medium div,
|
||
.marker-cluster-large div {
|
||
background: transparent !important;
|
||
}
|
||
|
||
/* ── Leaflet z-index ───────────────────────────── */
|
||
.leaflet-container {
|
||
z-index: 0;
|
||
}
|
||
.leaflet-pane {
|
||
z-index: 0 !important;
|
||
}
|
||
.leaflet-top, .leaflet-bottom {
|
||
z-index: 1 !important;
|
||
}
|
||
|
||
/* ── iOS-style map tooltip ─────────────────────── */
|
||
.leaflet-tooltip.map-tooltip {
|
||
background: var(--tooltip-bg);
|
||
backdrop-filter: blur(8px);
|
||
-webkit-backdrop-filter: blur(8px);
|
||
border: none;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||
padding: 5px 10px;
|
||
font-family: var(--font-system);
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
pointer-events: none;
|
||
color: var(--text-primary);
|
||
}
|
||
.leaflet-tooltip.map-tooltip::before,
|
||
.leaflet-tooltip-left.map-tooltip::before,
|
||
.leaflet-tooltip-top.map-tooltip::before {
|
||
display: none;
|
||
}
|
||
|
||
/* Scrollbars — styled on desktop, hidden on mobile */
|
||
::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
::-webkit-scrollbar-button {
|
||
display: none;
|
||
height: 0;
|
||
width: 0;
|
||
}
|
||
::-webkit-scrollbar-track {
|
||
background: var(--scrollbar-track);
|
||
border-radius: 3px;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
background: var(--scrollbar-thumb);
|
||
border-radius: 3px;
|
||
}
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: var(--scrollbar-hover);
|
||
}
|
||
|
||
@media (max-width: 767px), (pointer: coarse) and (max-height: 500px) {
|
||
* { scrollbar-width: none; }
|
||
::-webkit-scrollbar { width: 0; height: 0; }
|
||
}
|
||
|
||
.route-info-pill { background: none !important; border: none !important; box-shadow: none !important; width: auto !important; height: auto !important; margin: 0 !important; }
|
||
.chat-scroll { overflow-y: auto !important; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
|
||
.chat-scroll::-webkit-scrollbar { width: 0; background: transparent; }
|
||
|
||
|
||
/* Einheitliche Formular-Inputs */
|
||
.form-input {
|
||
width: 100%;
|
||
border: 1px solid var(--border-primary);
|
||
border-radius: 10px;
|
||
padding: 8px 14px;
|
||
font-size: 13px;
|
||
font-family: inherit;
|
||
outline: none;
|
||
box-sizing: border-box;
|
||
color: var(--text-primary);
|
||
background: var(--bg-input);
|
||
transition: border-color 0.15s;
|
||
}
|
||
.form-input:focus {
|
||
border-color: var(--text-faint);
|
||
}
|
||
.form-input::placeholder {
|
||
color: var(--text-faint);
|
||
}
|
||
|
||
/* Brand images: no save/copy/drag */
|
||
img[alt="TREK"] {
|
||
pointer-events: none;
|
||
user-select: none;
|
||
-webkit-user-select: none;
|
||
-webkit-user-drag: none;
|
||
-webkit-touch-callout: none;
|
||
}
|
||
|
||
/* Weiche Übergänge */
|
||
.transition-smooth {
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
|
||
|
||
/* Tagesliste */
|
||
.day-list-item {
|
||
transition: background-color 0.15s ease;
|
||
}
|
||
|
||
.day-list-item:hover {
|
||
background-color: var(--bg-secondary);
|
||
}
|
||
|
||
.day-list-item.active {
|
||
background-color: #4f46e5;
|
||
color: white;
|
||
}
|
||
|
||
.scroll-container {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
||
}
|
||
|
||
/* ── Dark-Mode: Tailwind-Klassen ─────────────── */
|
||
.dark .bg-slate-50 { background-color: #131316 !important; }
|
||
.dark .bg-slate-100 { background-color: #1c1c21 !important; }
|
||
.dark .bg-white { background-color: #09090b !important; }
|
||
.dark .bg-gray-50 { background-color: #131316 !important; }
|
||
.dark .bg-gray-100 { background-color: #1c1c21 !important; }
|
||
.dark .text-slate-900, .dark .text-gray-900, .dark .text-slate-800 { color: #f4f4f5 !important; }
|
||
.dark .text-slate-700, .dark .text-gray-700 { color: #d4d4d8 !important; }
|
||
.dark .text-slate-600, .dark .text-gray-600 { color: #a1a1aa !important; }
|
||
.dark .text-slate-500, .dark .text-gray-500 { color: #a1a1aa !important; }
|
||
.dark .text-slate-400, .dark .text-gray-400 { color: #71717a !important; }
|
||
.dark .border-slate-200, .dark .border-gray-200, .dark .border-slate-300 { border-color: #27272a !important; }
|
||
.dark .border-gray-100, .dark .border-slate-100 { border-color: #1c1c21 !important; }
|
||
.dark .hover\:bg-slate-50:hover, .dark .hover\:bg-gray-50:hover { background-color: #1c1c21 !important; }
|
||
.dark .bg-slate-50\/60 { background-color: transparent !important; }
|
||
.dark .divide-slate-100 > :not([hidden]) ~ :not([hidden]) { border-color: #27272a !important; }
|
||
.dark .bg-slate-100 { background-color: #27272a !important; }
|
||
.dark .bg-slate-900 { background-color: #e4e4e7 !important; color: #09090b !important; }
|
||
.dark .hover\:bg-slate-700:hover { background-color: #d4d4d8 !important; color: #09090b !important; }
|
||
.dark input, .dark textarea, .dark select {
|
||
background-color: #1c1c21;
|
||
color: #f4f4f5;
|
||
border-color: #27272a;
|
||
}
|
||
.dark input::placeholder, .dark textarea::placeholder { color: #71717a; }
|
||
.dark .bg-emerald-50 { background-color: rgba(16,185,129,0.15) !important; }
|
||
.dark .bg-red-50 { background-color: rgba(239,68,68,0.15) !important; }
|
||
|
||
/* Ladebildschirm */
|
||
.dark .min-h-screen { background-color: var(--bg-primary) !important; }
|
||
|
||
/* Modal-Hintergrund */
|
||
.dark .trek-modal-backdrop { background: rgba(0,0,0,0.6); }
|
||
|
||
/* ── Dark: Fallback für Komponenten die noch nicht auf CSS-Variablen umgestellt sind ── */
|
||
.dark {
|
||
color-scheme: dark;
|
||
}
|
||
|
||
|
||
/* Toast-Animationen */
|
||
@keyframes slideUp {
|
||
from { transform: translateY(100%); }
|
||
to { transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes slide-in-right {
|
||
from { transform: translateX(100%); opacity: 0; }
|
||
to { transform: translateX(0); opacity: 1; }
|
||
}
|
||
|
||
@keyframes slide-out-right {
|
||
from { transform: translateX(0); opacity: 1; }
|
||
to { transform: translateX(100%); opacity: 0; }
|
||
}
|
||
|
||
.toast-enter {
|
||
animation: slide-in-right 0.3s ease-out forwards;
|
||
}
|
||
|
||
.toast-exit {
|
||
animation: slide-out-right 0.3s ease-in forwards;
|
||
}
|
||
|
||
/* Modal-Hintergrund (eigener Namespace, sonst blenden Content-Blocker .modal-backdrop aus) */
|
||
.trek-modal-backdrop {
|
||
backdrop-filter: blur(4px);
|
||
}
|
||
|
||
/* Fortschrittsbalken */
|
||
@keyframes progress-fill {
|
||
from { width: 0; }
|
||
to { width: var(--progress); }
|
||
}
|
||
|
||
/* Anmeldeseiten-Gradient */
|
||
.auth-gradient {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
}
|
||
|
||
/* Bildanzeige-Overlay */
|
||
.lightbox-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0, 0, 0, 0.95);
|
||
z-index: 1000;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* Shared links in the Collab tab: one chip per link, two to a row across the full
|
||
width, one to a row where the panel is narrow. The chip is the link; edit, pin and
|
||
delete sit quietly in its tail and come forward on hover or keyboard focus. They
|
||
keep their width throughout: unfolding them reflowed the row under the pointer,
|
||
and a chip that grows as you reach for it is a chip you cannot catch. */
|
||
.collab-link-grid {
|
||
container-type: inline-size;
|
||
}
|
||
.collab-link-grid__cells {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 8px;
|
||
}
|
||
@container (max-width: 420px) {
|
||
.collab-link-grid__cells { grid-template-columns: minmax(0, 1fr); }
|
||
}
|
||
.collab-link-chip {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
min-width: 0;
|
||
padding-block: 5px;
|
||
padding-inline: 5px 8px;
|
||
/* Half the one-line height, so a one-line chip is still a pill and one whose
|
||
title wraps (#2550) keeps its rounded corners instead of a stadium end. */
|
||
border-radius: 18px;
|
||
background: var(--bg-secondary);
|
||
border: 1px solid var(--border-faint);
|
||
box-shadow: var(--shadow-sm);
|
||
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
|
||
}
|
||
.collab-link-chip:hover, .collab-link-chip:focus-within {
|
||
transform: translateY(-1px);
|
||
box-shadow: var(--shadow-md);
|
||
border-color: var(--border-primary);
|
||
}
|
||
.collab-link-chip--pinned {
|
||
background: var(--accent-subtle);
|
||
border-color: color-mix(in srgb, var(--accent) 35%, transparent);
|
||
}
|
||
.collab-link-chip__main {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 7px;
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
outline: none;
|
||
}
|
||
.collab-link-chip__icon {
|
||
position: relative;
|
||
display: grid;
|
||
place-items: center;
|
||
width: 24px;
|
||
height: 24px;
|
||
flex: 0 0 24px;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
background: var(--bg-card);
|
||
color: var(--text-faint);
|
||
}
|
||
.collab-link-chip__icon img { width: 16px; height: 16px; object-fit: contain; }
|
||
.collab-link-chip__icon > * { transition: opacity 0.15s ease; }
|
||
/* Reaching for the chip turns its favicon into the open-in-new-tab glyph: the
|
||
affordance without a glyph of its own taking room from the title. */
|
||
.collab-link-chip__go {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: grid;
|
||
place-items: center;
|
||
color: var(--text-primary);
|
||
opacity: 0;
|
||
}
|
||
.collab-link-chip__main:hover .collab-link-chip__go, .collab-link-chip__main:focus-visible .collab-link-chip__go { opacity: 1; }
|
||
.collab-link-chip__main:hover .collab-link-chip__icon > :not(.collab-link-chip__go),
|
||
.collab-link-chip__main:focus-visible .collab-link-chip__icon > :not(.collab-link-chip__go) { opacity: 0; }
|
||
/* The title is never cut (#2550): it wraps and the chip grows in height, so the
|
||
full name is on screen on every device, not behind a tooltip a phone has no
|
||
way to show. The host sits beside a title short enough to leave room and drops
|
||
to its own line below one that wraps; only the host is allowed an ellipsis. */
|
||
.collab-link-chip__text {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
column-gap: 6px;
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
.collab-link-chip__title {
|
||
font-size: calc(12.5px * var(--fs-scale-body, 1));
|
||
font-weight: 600;
|
||
line-height: 1.25;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
}
|
||
.collab-link-chip__host {
|
||
font-size: calc(10.5px * var(--fs-scale-caption, 1));
|
||
color: var(--text-faint);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
}
|
||
.collab-link-chip__actions {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 0 0 auto;
|
||
gap: 1px;
|
||
margin-inline-start: auto;
|
||
opacity: 0.45;
|
||
transition: opacity 0.15s ease;
|
||
}
|
||
.collab-link-chip:hover .collab-link-chip__actions, .collab-link-chip:focus-within .collab-link-chip__actions { opacity: 1; }
|
||
.collab-link-chip__action {
|
||
display: grid;
|
||
place-items: center;
|
||
width: 22px;
|
||
height: 22px;
|
||
border: 0;
|
||
border-radius: 50%;
|
||
background: transparent;
|
||
color: var(--text-faint);
|
||
cursor: pointer;
|
||
transition: background-color 0.15s ease, color 0.15s ease;
|
||
}
|
||
.collab-link-chip__action:hover, .collab-link-chip__action:focus-visible { background: var(--bg-hover); color: var(--text-primary); }
|
||
.collab-link-chip__action--on { color: var(--accent); }
|
||
/* Fingers, not a pointer: the phone Collab tab mounts this same chip. 22px targets
|
||
a pixel apart put delete one slip away from pin, so the hit box grows to the
|
||
30px at a 34px pitch the phone Notes tab uses. The vertical margin gives the
|
||
height back, the chip stays 36px. */
|
||
@media (hover: none) {
|
||
.collab-link-chip__actions { opacity: 1; gap: 4px; }
|
||
.collab-link-chip__action { width: 30px; height: 30px; margin: -4px 0; }
|
||
}
|
||
|
||
/* The desktop's links panel is tinted like the other Collab panels, so a chip sits on
|
||
the card colour as a card, the way notes and polls do; the phone keeps its pill. */
|
||
.collab-link-grid--desk .collab-link-chip {
|
||
padding: 8px 8px 8px 8px;
|
||
border-radius: 14px;
|
||
background: var(--bg-card);
|
||
box-shadow: none;
|
||
}
|
||
.collab-link-grid--desk .collab-link-chip:hover, .collab-link-grid--desk .collab-link-chip:focus-within {
|
||
transform: none;
|
||
box-shadow: var(--shadow-md);
|
||
}
|
||
.collab-link-grid--desk .collab-link-chip--pinned { background: var(--accent-subtle); }
|
||
.collab-link-grid--desk .collab-link-chip__icon { width: 28px; height: 28px; flex-basis: 28px; border-radius: 9px; background: var(--bg-secondary); }
|
||
|
||
/* Markdown in Collab Notes */
|
||
.collab-note-md strong, .collab-note-md-full strong { font-weight: 700 !important; }
|
||
.collab-note-md, .collab-note-md-full { overflow-wrap: anywhere; }
|
||
.collab-note-md em, .collab-note-md-full em { font-style: italic !important; }
|
||
.collab-note-md h1, .collab-note-md h2, .collab-note-md h3 { font-weight: 700 !important; margin: 0; }
|
||
.collab-note-md-full h1 { font-size: 1.3em !important; font-weight: 700 !important; margin: 0.8em 0 0.3em; }
|
||
.collab-note-md-full h2 { font-size: 1.15em !important; font-weight: 700 !important; margin: 0.8em 0 0.3em; }
|
||
.collab-note-md-full h3 { font-size: 1em !important; font-weight: 700 !important; margin: 0.8em 0 0.3em; }
|
||
.collab-note-md p, .collab-note-md-full p { margin: 0 0 0.3em; }
|
||
.collab-note-md p:last-child, .collab-note-md-full p:last-child { margin-bottom: 0; }
|
||
.collab-note-md ul, .collab-note-md-full ul { list-style-type: disc !important; padding-inline-start: 1.4em !important; margin: 0.2em 0; }
|
||
.collab-note-md ol, .collab-note-md-full ol { list-style-type: decimal !important; padding-inline-start: 1.4em !important; margin: 0.2em 0; }
|
||
.collab-note-md li, .collab-note-md-full li { display: list-item !important; margin: 0.1em 0; }
|
||
.collab-note-md code, .collab-note-md-full code { font-size: 0.9em; padding: 1px 5px; border-radius: 4px; background: var(--bg-secondary); word-break: break-all; }
|
||
.collab-note-md-full pre { padding: 10px 12px; border-radius: 8px; background: var(--bg-secondary); overflow-x: auto; margin: 0.5em 0; }
|
||
.collab-note-md-full pre code { padding: 0; background: none; }
|
||
.collab-note-md a, .collab-note-md-full a { color: #3b82f6; text-decoration: underline; word-break: normal; overflow-wrap: break-word; }
|
||
.collab-note-md blockquote, .collab-note-md-full blockquote { border-inline-start: 3px solid var(--border-primary); padding-inline-start: 12px; margin: 0.5em 0; color: var(--text-muted); }
|
||
.collab-note-md-full table { border-collapse: collapse; width: 100%; margin: 0.5em 0; }
|
||
.collab-note-md-full th, .collab-note-md-full td { border: 1px solid var(--border-primary); padding: 4px 8px; font-size: 0.9em; }
|
||
.collab-note-md-full hr { border: none; border-top: 1px solid var(--border-primary); margin: 0.8em 0; }
|
||
|
||
/* The day head's "+" (place, transport, transit, note): shown while the head is
|
||
hovered, focused or the day is open. Touch devices (hover: none) keep it visible. */
|
||
@media (hover: hover) {
|
||
.dp-day-tools { opacity: 0; transition: opacity 0.12s ease; }
|
||
.dp-day-header:hover .dp-day-tools,
|
||
.dp-day-header:focus-within .dp-day-tools,
|
||
.dp-day-header[data-selected="true"] .dp-day-tools { opacity: 1; }
|
||
}
|
||
|
||
/* Planner rows a finger can pick up (#1616). A long press is what arms the drag,
|
||
and that is the same gesture the browser answers with a text selection and a
|
||
callout — "the action is interpreted as a text selection" in the report. Only
|
||
on coarse pointers, and only inside the two panes that opt into the bridge. */
|
||
@media (pointer: coarse) {
|
||
[data-touch-drag] [draggable='true'] {
|
||
-webkit-touch-callout: none;
|
||
-webkit-user-select: none;
|
||
user-select: none;
|
||
}
|
||
}
|
||
|
||
/* ── TREK range slider — thin filled track + soft round thumb ─────────────── */
|
||
.trek-range {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
width: 100%;
|
||
height: 5px;
|
||
border-radius: 999px;
|
||
background: linear-gradient(to right, var(--accent) var(--fill, 50%), var(--border-faint) var(--fill, 50%));
|
||
outline: none;
|
||
cursor: pointer;
|
||
}
|
||
.trek-range::-webkit-slider-thumb {
|
||
-webkit-appearance: none;
|
||
width: 18px;
|
||
height: 18px;
|
||
border-radius: 50%;
|
||
background: var(--bg-card, #fff);
|
||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
|
||
transition: transform 0.12s ease;
|
||
}
|
||
.trek-range::-webkit-slider-thumb:hover { transform: scale(1.1); }
|
||
.trek-range::-webkit-slider-thumb:active { transform: scale(1.18); }
|
||
.trek-range::-moz-range-thumb {
|
||
width: 18px;
|
||
height: 18px;
|
||
border-radius: 50%;
|
||
background: var(--bg-card, #fff);
|
||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
|
||
}
|
||
.trek-range::-moz-range-track { height: 5px; border-radius: 999px; background: transparent; }
|