From 87394a44ebb0fb3d75fe1a34a920af163f45949d Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 18 Dec 2025 23:40:44 -0500 Subject: [PATCH] pewsqie pardair --- src/components/Map/markerManager.js | 482 ++++++++++----------------- src/components/Map/templateSpecs.js | 21 +- src/components/Map/useMapCore.js | 16 +- src/components/Map/usePostsEngine.js | 64 ++-- src/styles/map.css | 1 + src/styles/mapMarkers.css | 260 +++++++++++++++ src/styles/overlays.css | 11 +- 7 files changed, 499 insertions(+), 356 deletions(-) diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js index e974930..5158a35 100644 --- a/src/components/Map/markerManager.js +++ b/src/components/Map/markerManager.js @@ -7,69 +7,17 @@ import { cardTokens } from "../../theme/cardTokens"; import { getTemplateSpecForPost, adaptPostToTemplateData } from "./templateSpecs"; export function clearAllMarkers(markersRef, expandedElRef) { - // close overlay if any - try { - const el = expandedElRef?.current; - if (el && el.__swClose) el.__swClose(); - } catch {} - if (markersRef.current) { for (const m of markersRef.current) { - try { - if (m?.el && m.el.__swUnmount) m.el.__swUnmount(); - } catch {} - try { - m.marker.remove(); - } catch {} + try { if (m?.el && m.el.__swUnmount) m.el.__swUnmount(); } catch {} + try { m.marker.remove(); } catch {} } } markersRef.current = []; if (expandedElRef) expandedElRef.current = null; } -function rectsOverlap(a, b) { - return !(a.right < b.left || a.left > b.right || a.bottom < b.top || a.top > b.bottom); -} - -export function applyOcclusionForExpanded(markersRef, expandedEl) { - if (!expandedEl) return; - - const expandedCard = expandedEl.querySelector(".post-card"); - if (!expandedCard) return; - - const PAD_X = 26; - const PAD_TOP = 20; - const PAD_BOTTOM = 140; - - const raw = expandedCard.getBoundingClientRect(); - const expandedRect = { - left: raw.left - PAD_X, - right: raw.right + PAD_X, - top: raw.top - PAD_TOP, - bottom: raw.bottom + PAD_BOTTOM, - }; - - const list = markersRef.current || []; - for (const item of list) { - const el = item?.el; - if (!el) continue; - - const r = el.getBoundingClientRect(); - - // Only hide markers that are below/behind the expanded card - const isBelow = r.top >= raw.top; - if (!isBelow) { - el.style.visibility = "visible"; - el.style.pointerEvents = "auto"; - continue; - } - - const hide = rectsOverlap(expandedRect, r); - el.style.visibility = hide ? "hidden" : "visible"; - el.style.pointerEvents = hide ? "none" : "auto"; - } -} - +export function applyOcclusionForExpanded() {} export function clearOcclusion(markersRef) { const list = markersRef.current || []; for (const item of list) { @@ -91,9 +39,7 @@ function mountCard(container, spec, data, theme) { themeTokens: tokens, onAction: (action, value) => { if (action?.type === "open_url" && value) { - try { - window.open(value, "_blank"); - } catch {} + try { window.open(value, "_blank"); } catch {} } }, className: "", @@ -101,9 +47,7 @@ function mountCard(container, spec, data, theme) { ); return () => { - try { - root.unmount(); - } catch {} + try { root.unmount(); } catch {} }; } @@ -115,164 +59,193 @@ function escapeHtml(str) { .replace(/"/g, """); } -/** - * Overlay: fixed card at the bottom + pointer that follows the marker. - * We only pan the minimum if the marker ends up behind the card. - */ -function getOrCreateExpandedOverlay(map) { - const container = map.getContainer(); - if (!container) return null; +function normCatLabel(post) { + const c = (post?.category || "").toString().toUpperCase(); + if (c === "NEWS") return "NEWS"; + if (c === "EVENT" || c === "EVENTS") return "EVENTS"; + if (c === "FRIENDS") return "FRIENDS"; + if (c === "MARKET") return "MARKET"; + return "NEWS"; +} - let root = container.querySelector(".sw-expanded-overlay-root"); - if (root) return root; +function formatRelativeTime(createdAt) { + try { + if (!createdAt) return "now"; + const d = createdAt instanceof Date ? createdAt : new Date(String(createdAt)); + if (Number.isNaN(d.getTime())) return "now"; - root = document.createElement("div"); - root.className = "sw-expanded-overlay-root"; + const s = Math.floor((Date.now() - d.getTime()) / 1000); + if (s < 60) return "now"; + const m = Math.floor(s / 60); + if (m < 60) return `${m}m`; + const h = Math.floor(m / 60); + if (h < 48) return `${h}h`; + const days = Math.floor(h / 24); + return `${days}d`; + } catch { + return "now"; + } +} - root.innerHTML = ` -
-
-
-
-
-
-
-
Watching
-
-
☑ Julia ★
— I see it…
-
☑ Kim / CNN
— Blah blah blah
-
☑ Yan ★
— Watch this!!
-
- +function closeCenteredOverlay(map) { + try { + const ov = map?.__swCenteredOverlay; + if (!ov) return; + + if (ov.onKey) window.removeEventListener("keydown", ov.onKey); + try { if (ov.unmountFull) ov.unmountFull(); } catch {} + try { if (ov.unmountMini) ov.unmountMini(); } catch {} + try { ov.backdrop?.remove?.(); } catch {} + + map.__swCenteredOverlay = null; + } catch {} +} + +function openCenteredOverlay({ map, post, theme }) { + if (!map) return; + + closeCenteredOverlay(map); + + const data = adaptPostToTemplateData(post); + const cat = normCatLabel(post); + const relTime = formatRelativeTime(post?.created_at || post?.CreatedAt || post?.createdAt); + const author = (post?.author || post?.Author || "").toString().trim(); + const sub = (post?.sub_category || post?.subCategory || "").toString().trim(); + const metaLeft = author ? `by ${author}` : ""; + const metaRight = sub ? sub : ""; + const img = data?.image || ""; + const url = data?.url || ""; + + // Backdrop catches outside click to close + const backdrop = document.createElement("div"); + backdrop.style.position = "fixed"; + backdrop.style.inset = "0"; + backdrop.style.zIndex = "9999999"; + backdrop.style.background = "rgba(0,0,0,0.08)"; + backdrop.style.backdropFilter = "blur(1px)"; + backdrop.style.display = "flex"; + backdrop.style.alignItems = "center"; + backdrop.style.justifyContent = "center"; + backdrop.style.padding = "12px"; + backdrop.style.pointerEvents = "auto"; + + // Wrapper uses existing styling hooks + const modalWrap = document.createElement("div"); + modalWrap.className = "post-pin--expanded"; + modalWrap.style.pointerEvents = "auto"; + + // Keep size behavior from your CSS (max-width/min(92vw,...), max-height etc.) + modalWrap.innerHTML = ` +
+
+
+
${escapeHtml(cat)}
+
+ ${metaLeft ? `${escapeHtml(metaLeft)}` : ""} + ${metaRight ? `• ${escapeHtml(metaRight)}` : ""} + • ${escapeHtml(relTime)}
-
-
-
The news here (generated)
-
+ +
-
- - - - -
+
+ ${ + img + ? `` + : `
` + } +
-
-
Live chat / comments
-
-
— …
-
— what the f***?
-
— nice…
-
— my eyes!!
-
-
- - -
+
${escapeHtml(data?.headline || post?.title || "Untitled")}
+ +
+ ${escapeHtml(data?.summary || post?.snippet || "")} +
+ +
+
👁 12.4k
+
❤️ 1.3k
+
💬 248
+
📍 ${escapeHtml(post?.city || post?.location_name || "Nearby")}
+
+ +
+ + +
+ + + +
-
-
-
+
+
Live chat / comments
+
+
— …
+
— what the f***?
+
— nice…
+
— my eyes!!
+
+
+ + +
+
+ + + +
`; - // helper close fn - root.__swClose = () => { - try { - if (root.__swUnmountFull) root.__swUnmountFull(); - } catch {} - try { - if (root.__swMoveHandler && root.__swMap) { - root.__swMap.off("move", root.__swMoveHandler); - root.__swMap.off("zoom", root.__swMoveHandler); - } - } catch {} - try { - root.remove(); - } catch {} + backdrop.appendChild(modalWrap); + document.body.appendChild(backdrop); + + // outside click closes + backdrop.addEventListener("click", () => closeCenteredOverlay(map)); + // inside click doesn't close + modalWrap.addEventListener("click", (e) => e.stopPropagation()); + + // close button + const xbtn = modalWrap.querySelector(".sw-modal-x"); + if (xbtn) xbtn.addEventListener("click", () => closeCenteredOverlay(map)); + + // ESC closes + const onKey = (e) => { + if (e.key === "Escape") closeCenteredOverlay(map); }; + window.addEventListener("keydown", onKey); - container.appendChild(root); - return root; -} + // CTA open url + const cta = modalWrap.querySelector(".sw-cta-primary"); + if (cta) { + cta.addEventListener("click", () => { + const u = cta.getAttribute("data-url") || ""; + if (!u) return; + try { window.open(u, "_blank", "noopener,noreferrer"); } catch {} + }); + } -function updateOverlayPointer(map, overlayRoot, lngLat) { - if (!map || !overlayRoot || !lngLat) return; + // still mount template full (hidden) so you can switch back later instantly + let unmountFull = null; + const fullWrap = modalWrap.querySelector(".sw-template-full-wrap"); + if (fullWrap) { + const spec = getTemplateSpecForPost(post, "full"); + unmountFull = mountCard(fullWrap, spec, data, theme); + } - const p = map.project(lngLat); - const pointer = overlayRoot.querySelector(".sw-overlay-pointer"); - const line = overlayRoot.querySelector(".sw-overlay-line"); - const cardWrap = overlayRoot.querySelector(".sw-expanded-overlay-card"); - const card = overlayRoot.querySelector(".sw-expanded-overlay-card .post-card"); - - if (!pointer || !line || !cardWrap || !card) return; - - // pointer follows marker - pointer.style.left = `${p.x}px`; - pointer.style.top = `${p.y}px`; - - // simple line from marker to bottom center of the card - const contRect = map.getContainer().getBoundingClientRect(); - const cardRect = cardWrap.getBoundingClientRect(); - - const x1 = p.x; - const y1 = p.y; - - const x2 = (cardRect.left - contRect.left) + cardRect.width / 2; - const y2 = (cardRect.top - contRect.top) + cardRect.height; // bottom of card - - const dx = x2 - x1; - const dy = y2 - y1; - const len = Math.sqrt(dx * dx + dy * dy); - - // place a thin line - line.style.left = `${x1}px`; - line.style.top = `${y1}px`; - line.style.width = `${len}px`; - line.style.transform = `rotate(${Math.atan2(dy, dx)}rad)`; -} - -function minimalPanToKeepMarkerVisible(map, overlayRoot, lngLat) { - if (!map || !overlayRoot || !lngLat) return; - - const p = map.project(lngLat); - const cont = map.getContainer(); - if (!cont) return; - - const contRect = cont.getBoundingClientRect(); - const cardWrap = overlayRoot.querySelector(".sw-expanded-overlay-card"); - if (!cardWrap) return; - - const cardRect = cardWrap.getBoundingClientRect(); - - const topPad = 18; - const bottomSafe = (cardRect.top - contRect.top) - 18; - - let dy = 0; - - // marker too high -> bring it down a bit - if (p.y < topPad) dy = (topPad - p.y); - - // marker behind the card -> bring it up a bit - if (p.y > bottomSafe) dy = (bottomSafe - p.y); - - // clamp so it never "vent dalle trop loin" - dy = Math.max(-160, Math.min(160, dy)); - - if (Math.abs(dy) < 2) return; - - try { - const until = Date.now() + 2000; - map.__swIgnoreMapClickUntil = until; - map.__swIgnoreCloseUntil = until; - map.__swIgnoreFetchUntil = until; - map.__swIgnoreRebuildUntil = until; - } catch {} - - map.panBy([0, dy], { duration: 350 }); + map.__swCenteredOverlay = { + postId: post?.id, + backdrop, + onKey, + unmountFull, + unmountMini: null, + }; } export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, theme = "blue") { @@ -293,10 +266,7 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the ? post.lng : null; - if (lat == null || lon == null) { - console.warn("post sans coordonnée:", post); - return; - } + if (lat == null || lon == null) return; const lngLat = [lon, lat]; @@ -306,9 +276,7 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the function unmountIfAny() { if (root.__swUnmount) { - try { - root.__swUnmount(); - } catch {} + try { root.__swUnmount(); } catch {} root.__swUnmount = null; } } @@ -333,88 +301,6 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the clearOcclusion(markersRef); } - function closeExpandedOverlayIfAny() { - const cur = expandedElRef.current; - if (cur && cur.__swClose) { - try { - cur.__swClose(); - } catch {} - } - expandedElRef.current = null; - clearOcclusion(markersRef); - } - - function renderExpandedOverlay() { - // locks so fetch/rebuild doesn't close while opening - try { - const until = Date.now() + 2000; - map.__swIgnoreMapClickUntil = until; - map.__swIgnoreCloseUntil = until; - map.__swIgnoreFetchUntil = until; - map.__swIgnoreRebuildUntil = until; - } catch {} - - closeExpandedOverlayIfAny(); - - const overlayRoot = getOrCreateExpandedOverlay(map); - if (!overlayRoot) return; - - // fill headline - const headline = escapeHtml(post?.title || "Untitled"); - const titleEl = overlayRoot.querySelector(".sw-news-title"); - if (titleEl) titleEl.innerHTML = headline; - - // mount template full into the left area - try { - if (overlayRoot.__swUnmountFull) overlayRoot.__swUnmountFull(); - } catch {} - overlayRoot.__swUnmountFull = null; - - const wrap = overlayRoot.querySelector(".sw-template-full-wrap"); - if (wrap) { - wrap.innerHTML = ""; // ensure clean - const spec = getTemplateSpecForPost(post, "full"); - const data = adaptPostToTemplateData(post); - overlayRoot.__swUnmountFull = mountCard(wrap, spec, data, theme); - } - - // pointer updater - const moveHandler = () => { - try { - updateOverlayPointer(map, overlayRoot, lngLat); - applyOcclusionForExpanded(markersRef, overlayRoot); - } catch {} - }; - - overlayRoot.__swMap = map; - overlayRoot.__swMoveHandler = moveHandler; - - map.on("move", moveHandler); - map.on("zoom", moveHandler); - - // initial layout after paint - requestAnimationFrame(() => { - try { - // 1) update pointer/line - updateOverlayPointer(map, overlayRoot, lngLat); - - // 2) minimal pan so marker isn't hidden behind the fixed card - minimalPanToKeepMarkerVisible(map, overlayRoot, lngLat); - - // 3) occlusion now + after moveend - applyOcclusionForExpanded(markersRef, overlayRoot); - map.once("moveend", () => { - try { - updateOverlayPointer(map, overlayRoot, lngLat); - applyOcclusionForExpanded(markersRef, overlayRoot); - } catch {} - }); - } catch {} - }); - - expandedElRef.current = overlayRoot; - } - renderCompact(); root.__renderCompact = renderCompact; @@ -427,17 +313,13 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the root.addEventListener("click", (ev) => { ev.stopPropagation(); - // toggle: if overlay already open for this post, close it - const cur = expandedElRef.current; - if (cur && cur.__swForId && cur.__swForId === (post.id ?? `${lon},${lat}`)) { - closeExpandedOverlayIfAny(); + const existing = map.__swCenteredOverlay; + if (existing && existing.postId === post?.id) { + closeCenteredOverlay(map); return; } - // open overlay - renderExpandedOverlay(); - if (expandedElRef.current) { - expandedElRef.current.__swForId = post.id ?? `${lon},${lat}`; - } + openCenteredOverlay({ map, post, theme }); + if (expandedElRef) expandedElRef.current = null; }); } diff --git a/src/components/Map/templateSpecs.js b/src/components/Map/templateSpecs.js index 6cad01f..efd562a 100644 --- a/src/components/Map/templateSpecs.js +++ b/src/components/Map/templateSpecs.js @@ -7,7 +7,7 @@ export const TEMPLATE_SPECS = { news: { mini_spec: { - size: { w: 240, h: 96 }, /* ✅ smaller to avoid layout push */ + size: { w: 240, h: 96 }, background: { type: "gradient", value: "newsBlue" }, radius: 18, layers: [ @@ -16,23 +16,16 @@ export const TEMPLATE_SPECS = { { id: "meta", type: "text", x: 12, y: 76, w: 216, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 } ], }, + + // ✅ FULL: remove useless empty top (no hero image block), keep SAME overall size full_spec: { - size: { w: 360, h: 520 }, + size: { w: 360, h: 260 }, background: { type: "solid", value: "surface" }, radius: 22, layers: [ - { id: "hero", type: "image", x: 0, y: 0, w: 360, h: 220, bind: "data.image" }, - { id: "badge", type: "chip", x: 16, y: 16, text: "NEWS", style: "chip.news" }, - { id: "title", type: "text", x: 16, y: 240, w: 328, h: 78, bind: "data.headline", style: "text.h1", maxLines: 3 }, - { id: "summary", type: "text", x: 16, y: 325, w: 328, h: 110, bind: "data.summary", style: "text.body", maxLines: 5 }, - { - id: "cta", - type: "button", - x: 16, y: 450, w: 328, h: 48, - text: "Open source", - style: "button.primary", - action: { type: "open_url", bind: "data.url" } - } + { id: "badge", type: "chip", x: 16, y: 12, text: "NEWS", style: "chip.news" }, + { id: "title", type: "text", x: 16, y: 52, w: 328, h: 72, bind: "data.headline", style: "text.h1", maxLines: 3 }, + { id: "summary", type: "text", x: 16, y: 132, w: 328, h: 78, bind: "data.summary", style: "text.body", maxLines: 4 } ], }, }, diff --git a/src/components/Map/useMapCore.js b/src/components/Map/useMapCore.js index 546292e..7bfe4e6 100644 --- a/src/components/Map/useMapCore.js +++ b/src/components/Map/useMapCore.js @@ -105,21 +105,9 @@ export function useMapCore(theme) { map.on("zoom", reOcclude); // ignore close while auto actions are happening - map.on("click", () => { - try { - const until = map.__swIgnoreMapClickUntil || 0; - if (until && Date.now() < until) return; - } catch {} - closeExpandedIfAny(); - }); + map.on("click", () => { closeExpandedIfAny(); }); - map.on("dragstart", () => { - try { - const until = map.__swIgnoreCloseUntil || 0; - if (until && Date.now() < until) return; - } catch {} - closeExpandedIfAny(); - }); + map.on("dragstart", () => { closeExpandedIfAny(); }); map.on("load", () => setViewParams(getViewFromMap(map))); diff --git a/src/components/Map/usePostsEngine.js b/src/components/Map/usePostsEngine.js index 6b69fed..b63b226 100644 --- a/src/components/Map/usePostsEngine.js +++ b/src/components/Map/usePostsEngine.js @@ -22,21 +22,13 @@ export function usePostsEngine({ const [loadingPosts, setLoadingPosts] = useState(false); const [loadError, setLoadError] = useState(""); - const isExpandedOpen = () => { - try { - return !!expandedElRef?.current; - } catch { - return false; - } - }; - const rebuildMarkersForFilters = useCallback( (tf) => { const map = mapRef.current; if (!map) return; - // ✅ If expanded overlay/popup is open: NEVER rebuild markers (it closes the expanded) - if (isExpandedOpen()) return; + // ✅ If expanded popup is open, NEVER rebuild markers (rebuild would close it) + if (expandedElRef?.current) return; clearAllMarkers(markersRef, expandedElRef); @@ -62,7 +54,6 @@ export function usePostsEngine({ ); useEffect(() => { - // ✅ Sociowall list can still update via visiblePosts; markers rebuild is blocked while expanded open. rebuildMarkersForFilters(timeFilter); }, [timeFilter, mainFilter, subFilter, rebuildMarkersForFilters]); @@ -74,13 +65,26 @@ export function usePostsEngine({ let cancelled = false; async function load() { - // ✅ If expanded is open: delay fetch indefinitely until user closes it - if (isExpandedOpen()) { + const mapObj = mapRef.current; + + // ✅ If expanded popup is open: delay fetch (do NOT clear/rebuild while user reads) + if (expandedElRef?.current) { if (delayedFetchRef.current) clearTimeout(delayedFetchRef.current); - delayedFetchRef.current = setTimeout(load, 1200); + delayedFetchRef.current = setTimeout(load, 2500); return; } + // Also respect ignore window (during auto-pan) + try { + const until = mapObj?.__swIgnoreFetchUntil || 0; + if (until && Date.now() < until) { + const wait = Math.min(4000, until - Date.now()); + if (delayedFetchRef.current) clearTimeout(delayedFetchRef.current); + delayedFetchRef.current = setTimeout(load, wait + 25); + return; + } + } catch {} + const [lng, lat] = viewParams.center; const radiusKm = viewParams.radiusKm || 750; const filterKey = `${mainFilter}|${subFilter}`; @@ -93,6 +97,7 @@ export function usePostsEngine({ const lastR = last.radiusKm || 0; const radiusChanged = !lastR || Math.abs(radiusKm - lastR) / Math.max(lastR, 1) >= 0.25; + const minMoveKm = Math.max(50, radiusKm * 0.25); if (!radiusChanged && distKm < minMoveKm) return; @@ -132,9 +137,30 @@ export function usePostsEngine({ allPostsRef.current = Array.from(byId.values()); lastFetchRef.current = { center: [...viewParams.center], radiusKm, filterKey }; - // ✅ rebuild markers only if expanded is NOT open (double-safety) - if (!isExpandedOpen()) rebuildMarkersForFilters(timeFilter); + // If expanded popup is open, don't rebuild markers (keeps it open) + try { + if (expandedElRef?.current) { + const posts = allPostsRef.current || []; + const catCode2 = categoryCode(mainFilter); + const visible2 = posts.filter((p) => { + if (catCode2) { + const pc = (p.category || p.Category || "").toString().toUpperCase(); + if (pc !== catCode2) return false; + } + if (!matchesSubFilter(p, subFilter)) return false; + if (!matchesTimeFilter(p.created_at || p.CreatedAt, timeFilter)) return false; + return true; + }); + + setVisiblePosts(visible2); + setStatus(visible2.length ? "" : "No posts found."); + setLoadingPosts(false); + return; + } + } catch {} + + rebuildMarkersForFilters(timeFilter); setLoadingPosts(false); } catch (err) { console.error("Erreur chargement posts:", err); @@ -150,13 +176,11 @@ export function usePostsEngine({ return () => { cancelled = true; if (delayedFetchRef.current) { - try { - clearTimeout(delayedFetchRef.current); - } catch {} + try { clearTimeout(delayedFetchRef.current); } catch {} delayedFetchRef.current = null; } }; - }, [viewParams, mainFilter, subFilter, mapRef, rebuildMarkersForFilters, timeFilter]); + }, [viewParams, mainFilter, subFilter, mapRef, rebuildMarkersForFilters, timeFilter, expandedElRef]); const handleIncomingPost = useCallback( (p) => { diff --git a/src/styles/map.css b/src/styles/map.css index 1e88db9..c8a310e 100644 --- a/src/styles/map.css +++ b/src/styles/map.css @@ -1,4 +1,5 @@ :root{ + --sw-subcats-nudge: -35px; /* subcats vertical nudge */ --sw-below-overlap: 40px; /* wall overlaps map a bit */ } diff --git a/src/styles/mapMarkers.css b/src/styles/mapMarkers.css index 70cdc9f..391ea0e 100644 --- a/src/styles/mapMarkers.css +++ b/src/styles/mapMarkers.css @@ -247,3 +247,263 @@ display:none; } } + + +/* SW_FIX_FULL_WRAP_COLLAPSE:BEGIN */ +/* Fix: expanded template wrapper collapsing into a thin line */ +.post-pin--expanded .post-card.sw-expanded-shell{ + width: 360px !important; + min-width: 360px !important; + max-width: 360px !important; + height: 520px !important; + max-height: 520px !important; + overflow: hidden !important; /* no useless scroll */ +} + +.sw-template-full-wrap{ + display: block !important; + width: 360px !important; + height: 520px !important; +} + +.sw-template-full-wrap > *{ + display: block !important; +} +/* SW_FIX_FULL_WRAP_COLLAPSE:END */ + + + +/* ===== Expanded header (mock) ===== */ +.sw-expanded-badge{ + display:inline-flex; + align-items:center; + justify-content:center; + padding: 6px 10px; + border-radius: 999px; + font-weight: 900; + font-size: 12px; + background: rgba(56,189,248,0.18); + color: #D7F3FF; + width: fit-content; +} + +.sw-expanded-title{ + margin-top: 10px; + font-size: 26px; + font-weight: 900; + line-height: 1.05; + color: #F0F7FF; +} + +.sw-expanded-snippet{ + margin-top: 8px; + font-size: 14px; + font-weight: 700; + color: #B5C7E6; +} + +/* ========================================================= + SW_EXPANDED_POLISH (visual only) + - make expanded look like the mini gradients / modern UI + ========================================================= */ +.post-pin--expanded .post-card{ + background: linear-gradient(135deg, rgba(56,189,248,0.16), rgba(7,18,37,0.98) 55%, rgba(2,6,23,0.98)); + border: 1px solid rgba(96,165,250,0.75); + box-shadow: 0 18px 60px rgba(0,0,0,0.55), 0 0 24px rgba(56,189,248,0.22); +} + +.sw-expanded-badge{ + display:inline-flex; + align-items:center; + justify-content:center; + padding: 7px 12px; + border-radius: 999px; + font-weight: 900; + font-size: 12px; + letter-spacing: .04em; + color: #d7f3ff; + background: rgba(56,189,248,0.16); + border: 1px solid rgba(56,189,248,0.35); + margin-bottom: 10px; +} + +.sw-expanded-title{ + font-size: 28px; + font-weight: 950; + line-height: 1.05; + color: #f0f7ff; + margin-bottom: 8px; +} + +.sw-expanded-meta{ + font-size: 12px; + font-weight: 700; + color: rgba(181,199,230,0.95); + margin-bottom: 10px; +} + +.sw-expanded-snippet{ + font-size: 14px; + line-height: 1.35; + color: rgba(215,233,255,0.95); + background: rgba(3,14,32,0.45); + border: 1px solid rgba(90,190,255,0.22); + border-radius: 14px; + padding: 10px 12px; +} + +.post-card-btn{ + background: rgba(56,189,248,0.14); + border: 1px solid rgba(56,189,248,0.35); + color: #e8f5ff; + font-weight: 900; + padding: 6px 12px; +} + +.post-card-btn:active{ + transform: scale(0.98); +} + +/* ===== CENTER MODAL CONTENT (pretty + consistent) ===== */ +.sw-modal{ + padding: 12px 14px; +} + +.sw-modal-toprow{ + display:flex; + align-items:flex-start; + justify-content:space-between; + gap:10px; + margin-bottom: 10px; +} + +.sw-modal-badge{ + display:inline-flex; + align-items:center; + justify-content:center; + padding: 6px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 900; + letter-spacing: .06em; + background: rgba(56,189,248,0.16); + border: 1px solid rgba(90, 190, 255, 0.28); + color:#D7F3FF; + text-transform: uppercase; +} + +.sw-modal-meta{ + margin-top: 6px; + font-size: 11px; + color:#9eb7ff; + opacity:.95; + display:flex; + gap:6px; + flex-wrap:wrap; +} + +.sw-modal-x{ + border:none; + border-radius:999px; + width:34px; + height:34px; + background: rgba(3,14,32,0.55); + color:#e8f5ff; + font-weight:900; + cursor:pointer; + border:1px solid rgba(90,190,255,0.25); + flex-shrink:0; +} + +.sw-modal-hero{ + width:100%; + height: 180px; + border-radius: 16px; + overflow:hidden; + border: 1px solid rgba(90, 190, 255, 0.22); + background: radial-gradient(circle at 30% 30%, rgba(56,189,248,0.18), rgba(3,14,32,0.85)); + box-shadow: 0 10px 24px rgba(0,0,0,0.35); + margin-bottom: 10px; +} + +.sw-modal-hero img{ + width:100%; + height:100%; + object-fit: cover; + display:block; +} + +.sw-modal-hero-fallback{ + width:100%; + height:100%; + background: radial-gradient(circle at 35% 35%, rgba(56,189,248,0.22), rgba(3,14,32,0.85)); +} + +.sw-modal-title{ + font-size: 22px; + font-weight: 900; + line-height: 26px; + color:#F0F7FF; + margin: 6px 0 8px; +} + +.sw-modal-summary{ + font-size: 13px; + line-height: 17px; + color:#D7E9FF; + opacity:.95; + margin-bottom: 8px; +} + +.sw-stat-row{ + display:flex; + gap:8px; + flex-wrap:wrap; + margin: 6px 0 10px; +} + +.sw-stat-pill{ + font-size: 11px; + color:#c7e3ff; + background: rgba(3, 14, 32, 0.58); + border: 1px solid rgba(90, 190, 255, 0.22); + padding: 6px 10px; + border-radius: 999px; +} + +.sw-modal-cta-row{ + display:flex; + flex-direction:column; + gap:10px; + margin-bottom: 10px; +} + +.sw-cta-primary{ + width:100%; + border:none; + border-radius: 14px; + padding: 14px 12px; + font-weight: 900; + font-size: 14px; + color:#e8f5ff; + cursor:pointer; + background: rgba(56,189,248,0.22); + box-shadow: 0 10px 18px rgba(0,0,0,0.35); +} + +.sw-cta-primary:disabled{ + opacity:.55; + cursor:default; +} + +.sw-cta-actions{ + display:flex; + gap:8px; + flex-wrap:wrap; +} + +/* mobile tighten */ +@media (max-width: 640px){ + .sw-modal-hero{ height: 150px; } + .sw-modal-title{ font-size: 20px; line-height: 24px; } +} diff --git a/src/styles/overlays.css b/src/styles/overlays.css index b910a33..61bc6f6 100644 --- a/src/styles/overlays.css +++ b/src/styles/overlays.css @@ -15,14 +15,9 @@ display:flex; flex-direction:column; gap:.5rem; } -/* ✅ sub-cats: keep above wall overlap + moved DOWN by 10px more */ -.map-overlay-bottom{ - bottom: calc(2.6rem + var(--sw-below-overlap, 0px) + env(safe-area-inset-bottom) - 20px); - left:50%; - transform:translateX(-50%); - display:flex; - gap:.4rem; -} +/* ✅ Sub-categories row MUST stay above the Sociowall overlap */ +/* ✅ DESCEND 25px (was 0 / 10 / 20): */ +.map-overlay-bottom { bottom: calc(3.2rem + var(--sw-below-overlap, 0px) + env(safe-area-inset-bottom) + var(--sw-subcats-nudge, 0px)); left:50%; transform:translateX(-50%); display:flex; gap:.4rem; } .map-overlay-myloc { right:.7rem; bottom:6rem; }