pewsqie pardair

This commit is contained in:
Your Name 2025-12-18 23:40:44 -05:00
parent 68c3648768
commit 87394a44eb
7 changed files with 499 additions and 356 deletions

View File

@ -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,48 +59,129 @@ function escapeHtml(str) {
.replace(/"/g, "&quot;");
}
/**
* 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 = `
<div class="sw-expanded-overlay-card">
<div class="post-card sw-expanded-shell">
<div class="sw-expanded-top">
<div class="sw-expanded-left">
<div class="sw-template-full-wrap"></div>
</div>
<div class="sw-expanded-right">
<div class="sw-watch-title">Watching</div>
<div class="sw-watch-list">
<div class="sw-watch-item"> Julia <div class="sw-watch-msg"> I see it</div></div>
<div class="sw-watch-item"> Kim / CNN<div class="sw-watch-msg"> Blah blah blah</div></div>
<div class="sw-watch-item"> Yan <div class="sw-watch-msg"> Watch this!!</div></div>
</div>
<button class="sw-add-feed-btn" type="button">ADD TO FEED</button>
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 = `
<div class="post-card sw-expanded-shell sw-modal">
<div class="sw-modal-toprow">
<div class="sw-modal-left">
<div class="sw-modal-badge">${escapeHtml(cat)}</div>
<div class="sw-modal-meta">
${metaLeft ? `<span>${escapeHtml(metaLeft)}</span>` : ""}
${metaRight ? `<span>• ${escapeHtml(metaRight)}</span>` : ""}
<span> ${escapeHtml(relTime)}</span>
</div>
</div>
<div class="sw-news-generated">
<div class="sw-news-title"></div>
<div class="sw-news-sub">The news here (generated)</div>
<button class="sw-modal-x" type="button" aria-label="Close"></button>
</div>
<div class="post-card-footer sw-actions-row">
<button class="post-card-btn">Contact</button>
<button class="post-card-btn">Like</button>
<button class="post-card-btn">Share</button>
<button class="post-card-btn">Fix</button>
<div class="sw-modal-hero">
${
img
? `<img src="${escapeHtml(img)}" alt="" loading="lazy" />`
: `<div class="sw-modal-hero-fallback"></div>`
}
</div>
<div class="sw-modal-title">${escapeHtml(data?.headline || post?.title || "Untitled")}</div>
<div class="sw-modal-summary">
${escapeHtml(data?.summary || post?.snippet || "")}
</div>
<div class="sw-stat-row">
<div class="sw-stat-pill">👁 12.4k</div>
<div class="sw-stat-pill"> 1.3k</div>
<div class="sw-stat-pill">💬 248</div>
<div class="sw-stat-pill">📍 ${escapeHtml(post?.city || post?.location_name || "Nearby")}</div>
</div>
<div class="sw-modal-cta-row">
<button class="sw-cta-primary" type="button" ${url ? "" : "disabled"} data-url="${escapeHtml(url)}">
Open source
</button>
<div class="sw-cta-actions">
<button class="post-card-btn" type="button">Contact</button>
<button class="post-card-btn" type="button">Like</button>
<button class="post-card-btn" type="button">Share</button>
<button class="post-card-btn" type="button">Fix</button>
</div>
</div>
<div class="post-card-comments sw-livechat">
@ -172,107 +197,55 @@ function getOrCreateExpandedOverlay(map) {
<button class="sw-livechat-post" type="button">POST</button>
</div>
</div>
</div>
</div>
<div class="sw-overlay-line"></div>
<div class="sw-overlay-pointer"></div>
<!-- (Optional) keep template renderer mounted but hidden for future -->
<div class="sw-template-full-wrap" style="display:none;"></div>
</div>
`;
// 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;
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)`;
// 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);
}
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;
});
}

View File

@ -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 }
],
},
},

View File

@ -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)));

View File

@ -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) => {

View File

@ -1,4 +1,5 @@
:root{
--sw-subcats-nudge: -35px; /* subcats vertical nudge */
--sw-below-overlap: 40px; /* wall overlaps map a bit */
}

View File

@ -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; }
}

View File

@ -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; }