update frontend

This commit is contained in:
Your Name 2025-12-18 22:27:12 -05:00
parent d7f12db601
commit ef79700865
6 changed files with 346 additions and 603 deletions

View File

@ -7,6 +7,12 @@ import { cardTokens } from "../../theme/cardTokens";
import { getTemplateSpecForPost, adaptPostToTemplateData } from "./templateSpecs"; import { getTemplateSpecForPost, adaptPostToTemplateData } from "./templateSpecs";
export function clearAllMarkers(markersRef, expandedElRef) { export function clearAllMarkers(markersRef, expandedElRef) {
// close overlay if any
try {
const el = expandedElRef?.current;
if (el && el.__swClose) el.__swClose();
} catch {}
if (markersRef.current) { if (markersRef.current) {
for (const m of markersRef.current) { for (const m of markersRef.current) {
try { try {
@ -46,9 +52,11 @@ export function applyOcclusionForExpanded(markersRef, expandedEl) {
const list = markersRef.current || []; const list = markersRef.current || [];
for (const item of list) { for (const item of list) {
const el = item?.el; const el = item?.el;
if (!el || el === expandedEl) continue; if (!el) continue;
const r = el.getBoundingClientRect(); const r = el.getBoundingClientRect();
// Only hide markers that are below/behind the expanded card
const isBelow = r.top >= raw.top; const isBelow = r.top >= raw.top;
if (!isBelow) { if (!isBelow) {
el.style.visibility = "visible"; el.style.visibility = "visible";
@ -83,7 +91,9 @@ function mountCard(container, spec, data, theme) {
themeTokens: tokens, themeTokens: tokens,
onAction: (action, value) => { onAction: (action, value) => {
if (action?.type === "open_url" && value) { if (action?.type === "open_url" && value) {
try { window.open(value, "_blank"); } catch {} try {
window.open(value, "_blank");
} catch {}
} }
}, },
className: "", className: "",
@ -91,33 +101,169 @@ function mountCard(container, spec, data, theme) {
); );
return () => { return () => {
try { root.unmount(); } catch {} try {
root.unmount();
} catch {}
}; };
} }
function escapeHtml(str) {
return String(str ?? "")
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
/** /**
* Force the marker anchor to be displayed at: * Overlay: fixed card at the bottom + pointer that follows the marker.
* - X: centered (50% width) * We only pan the minimum if the marker ends up behind the card.
* - Y: 6/7 of map height (so the popup sits "1/7 from bottom")
*/ */
function panMarkerToPreferredAnchor(map, lngLat) { function getOrCreateExpandedOverlay(map) {
if (!map) return; const container = map.getContainer();
if (!container) return null;
const el = map.getContainer(); let root = container.querySelector(".sw-expanded-overlay-root");
if (!el) return; if (root) return root;
const rect = el.getBoundingClientRect(); root = document.createElement("div");
const targetX = rect.width * 0.5; root.className = "sw-expanded-overlay-root";
const targetY = rect.height * (6 / 7); // 1/7 from bottom => anchor at 6/7 height
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>
</div>
</div>
<div class="sw-news-generated">
<div class="sw-news-title"></div>
<div class="sw-news-sub">The news here (generated)</div>
</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>
<div class="post-card-comments sw-livechat">
<div class="sw-livechat-title">Live chat / comments</div>
<div class="sw-livechat-body">
<div class="sw-chat-line"> </div>
<div class="sw-chat-line"> what the f***?</div>
<div class="sw-chat-line"> nice</div>
<div class="sw-chat-line"> my eyes!!</div>
</div>
<div class="sw-livechat-inputrow">
<input class="sw-livechat-input" placeholder="Write a comment…" />
<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>
`;
// 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 {}
};
container.appendChild(root);
return root;
}
function updateOverlayPointer(map, overlayRoot, lngLat) {
if (!map || !overlayRoot || !lngLat) return;
const p = map.project(lngLat); const p = map.project(lngLat);
const dx = targetX - p.x; const pointer = overlayRoot.querySelector(".sw-overlay-pointer");
const dy = targetY - p.y; 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");
// tiny deltas -> skip if (!pointer || !line || !cardWrap || !card) return;
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) 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;
// lock close/fetch/rebuild while panning
try { try {
const until = Date.now() + 2000; const until = Date.now() + 2000;
map.__swIgnoreMapClickUntil = until; map.__swIgnoreMapClickUntil = until;
@ -126,7 +272,7 @@ function panMarkerToPreferredAnchor(map, lngLat) {
map.__swIgnoreRebuildUntil = until; map.__swIgnoreRebuildUntil = until;
} catch {} } catch {}
map.panBy([dx, dy], { duration: 650 }); map.panBy([0, dy], { duration: 350 });
} }
export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, theme = "blue") { export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, theme = "blue") {
@ -160,7 +306,9 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
function unmountIfAny() { function unmountIfAny() {
if (root.__swUnmount) { if (root.__swUnmount) {
try { root.__swUnmount(); } catch {} try {
root.__swUnmount();
} catch {}
root.__swUnmount = null; root.__swUnmount = null;
} }
} }
@ -185,97 +333,86 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
clearOcclusion(markersRef); clearOcclusion(markersRef);
} }
function renderExpanded() { function closeExpandedOverlayIfAny() {
unmountIfAny(); const cur = expandedElRef.current;
root.className = "post-pin post-pin--expanded"; if (cur && cur.__swClose) {
root.style.zIndex = "999999";
// 2000ms locks so recenter/fetch/rebuild does not close this expanded popup
try { try {
const m = mapRef.current; cur.__swClose();
if (m) { } catch {}
const until = Date.now() + 2000;
m.__swIgnoreMapClickUntil = until;
m.__swIgnoreCloseUntil = until;
m.__swIgnoreFetchUntil = until;
m.__swIgnoreRebuildUntil = until;
} }
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 {} } catch {}
closeExpandedOverlayIfAny();
const overlayRoot = getOrCreateExpandedOverlay(map);
if (!overlayRoot) return;
// fill headline
const headline = escapeHtml(post?.title || "Untitled"); const headline = escapeHtml(post?.title || "Untitled");
const titleEl = overlayRoot.querySelector(".sw-news-title");
if (titleEl) titleEl.innerHTML = headline;
root.innerHTML = ` // mount template full into the left area
<div class="post-card sw-expanded-shell"> try {
<div class="sw-expanded-top"> if (overlayRoot.__swUnmountFull) overlayRoot.__swUnmountFull();
<div class="sw-expanded-left"> } catch {}
<div class="sw-template-full-wrap"></div> overlayRoot.__swUnmountFull = null;
</div>
<div class="sw-expanded-right"> const wrap = overlayRoot.querySelector(".sw-template-full-wrap");
<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>
</div>
</div>
<div class="sw-news-generated">
<div class="sw-news-title">${headline}</div>
<div class="sw-news-sub">The news here (generated)</div>
</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>
<div class="post-card-comments sw-livechat">
<div class="sw-livechat-title">Live chat / comments</div>
<div class="sw-livechat-body">
<div class="sw-chat-line"> </div>
<div class="sw-chat-line"> what the f***?</div>
<div class="sw-chat-line"> nice</div>
<div class="sw-chat-line"> my eyes!!</div>
</div>
<div class="sw-livechat-inputrow">
<input class="sw-livechat-input" placeholder="Write a comment…" />
<button class="sw-livechat-post" type="button">POST</button>
</div>
</div>
</div>
<div class="post-card-pointer"></div>
`;
const wrap = root.querySelector(".sw-template-full-wrap");
if (wrap) { if (wrap) {
wrap.innerHTML = ""; // ensure clean
const spec = getTemplateSpecForPost(post, "full"); const spec = getTemplateSpecForPost(post, "full");
const data = adaptPostToTemplateData(post); const data = adaptPostToTemplateData(post);
root.__swUnmount = mountCard(wrap, spec, data, theme); overlayRoot.__swUnmountFull = mountCard(wrap, spec, data, theme);
} }
// After render, pan so marker sits at the preferred anchor (1/7 from bottom) // 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(() => { requestAnimationFrame(() => {
try { try {
const m = mapRef.current; // 1) update pointer/line
if (!m) return; updateOverlayPointer(map, overlayRoot, lngLat);
// 1) put the marker anchor exactly where we want // 2) minimal pan so marker isn't hidden behind the fixed card
panMarkerToPreferredAnchor(m, lngLat); minimalPanToKeepMarkerVisible(map, overlayRoot, lngLat);
// 2) re-occlude after the pan finishes // 3) occlusion now + after moveend
m.once("moveend", () => { applyOcclusionForExpanded(markersRef, overlayRoot);
try { applyOcclusionForExpanded(markersRef, root); } catch {} map.once("moveend", () => {
}); try {
updateOverlayPointer(map, overlayRoot, lngLat);
// also occlude right away applyOcclusionForExpanded(markersRef, overlayRoot);
applyOcclusionForExpanded(markersRef, root);
} catch {} } catch {}
}); });
} catch {}
});
expandedElRef.current = overlayRoot;
} }
renderCompact(); renderCompact();
@ -290,26 +427,17 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the
root.addEventListener("click", (ev) => { root.addEventListener("click", (ev) => {
ev.stopPropagation(); ev.stopPropagation();
if (expandedElRef.current && expandedElRef.current !== root) { // toggle: if overlay already open for this post, close it
if (expandedElRef.current.__renderCompact) expandedElRef.current.__renderCompact(); const cur = expandedElRef.current;
expandedElRef.current = null; if (cur && cur.__swForId && cur.__swForId === (post.id ?? `${lon},${lat}`)) {
closeExpandedOverlayIfAny();
return;
} }
const isExpanded = root.classList.contains("post-pin--expanded"); // open overlay
if (isExpanded) { renderExpandedOverlay();
renderCompact(); if (expandedElRef.current) {
expandedElRef.current = null; expandedElRef.current.__swForId = post.id ?? `${lon},${lat}`;
} else {
renderExpanded();
expandedElRef.current = root;
} }
}); });
} }
function escapeHtml(str) {
return String(str ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}

View File

@ -47,7 +47,23 @@ export function useMapCore(theme) {
function closeExpandedIfAny() { function closeExpandedIfAny() {
const el = expandedElRef.current; const el = expandedElRef.current;
if (el && el.__renderCompact) el.__renderCompact(); if (!el) return;
// overlay style
if (el.__swClose) {
try {
el.__swClose();
} catch {}
expandedElRef.current = null;
return;
}
// legacy marker-expanded style
if (el.__renderCompact) {
try {
el.__renderCompact();
} catch {}
}
expandedElRef.current = null; expandedElRef.current = null;
} }
@ -88,7 +104,7 @@ export function useMapCore(theme) {
map.on("move", reOcclude); map.on("move", reOcclude);
map.on("zoom", reOcclude); map.on("zoom", reOcclude);
// IMPORTANT: ignore close while we are auto-panning to keep popup visible // ignore close while auto actions are happening
map.on("click", () => { map.on("click", () => {
try { try {
const until = map.__swIgnoreMapClickUntil || 0; const until = map.__swIgnoreMapClickUntil || 0;

View File

@ -43,7 +43,7 @@
min-width:0; min-width:0;
} }
/* keep side-by-side on most phones; stack only ultra-small */ /* stack only ultra-small (NOT normal portrait phones) */
@media (max-width: 420px){ @media (max-width: 340px){
.below-row{ flex-direction:column; } .below-row{ flex-direction:column; }
} }

View File

@ -37,362 +37,86 @@
filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6)); filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
} }
.post-pin--expanded { z-index: 999999; } /* ===== Template mini wrapper (keeps pointer unchanged) ===== */
.sw-template-mini-wrap{
.post-pin--expanded .post-card {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
transform: translate(-50%, calc(-100% - 14px)); transform: translate(-50%, calc(-100% - 12px)) scale(0.62);
min-width: 260px; transform-origin: bottom center;
max-width: 320px; will-change: transform;
}
@media (max-width: 640px){
.sw-template-mini-wrap{
transform: translate(-50%, calc(-100% - 12px)) scale(0.52);
}
}
/* =========================================================
NEW: FIXED EXPANDED OVERLAY
- Card is fixed near bottom (consistent)
- Pointer follows marker (consistent)
- No huge map recenter jumps
========================================================= */
.sw-expanded-overlay-root{
position:absolute;
inset:0;
z-index: 999999;
pointer-events:none;
}
/* Card position: "dans le bas" (approx bottom zone), consistent on rotate */
.sw-expanded-overlay-card{
position:absolute;
left: 50%;
bottom: calc(12px + env(safe-area-inset-bottom) + var(--sw-below-overlap, 0px));
transform: translateX(-50%);
width: min(92vw, 420px);
pointer-events: auto;
}
/* Make card fit mobile nicely */
.sw-expanded-overlay-card .post-card{
width: 100%;
max-height: min(58vh, 640px);
overflow:auto;
-webkit-overflow-scrolling: touch;
background: rgba(8, 20, 40, 0.97); background: rgba(8, 20, 40, 0.97);
border-radius: 14px; border-radius: 14px;
padding: 10px 12px; padding: 10px 12px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.8); box-shadow: 0 10px 26px rgba(0, 0, 0, 0.85);
border: 1px solid rgba(90, 190, 255, 0.9); border: 1px solid rgba(90, 190, 255, 0.9);
color: #e8f5ff; color: #e8f5ff;
} }
.post-card-header { display:flex; justify-content:space-between; align-items:center; font-size:11px; margin-bottom:6px; }
.post-card-cat { font-weight:600; color:#8fc5ff; } /* Pointer on the marker position */
.post-card-time { color:#9eb7ff; } .sw-overlay-pointer{
.post-card-main { display:flex; flex-direction:row; gap:8px; margin-bottom:6px; }
.post-card-media {
width:72px; height:72px; border-radius:10px;
background: radial-gradient(circle at 30% 30%, #4a9dff, #07152b);
display:flex; align-items:center; justify-content:center; flex-shrink:0;
}
.post-card-media-label { font-size:9px; text-align:center; color:#e8f5ff; }
.post-card-info { flex:1; min-width:0; }
.post-card-title { font-size:13px; font-weight:600; margin-bottom:2px; }
.post-card-meta { font-size:10px; color:#9eb7ff; margin-bottom:4px; }
.post-card-body { font-size:11px; color:#c7e3ff; max-height:70px; overflow:hidden; }
.post-card-footer { display:flex; gap:6px; margin-bottom:6px; flex-wrap:wrap; }
.post-card-btn {
border:none; border-radius:999px; padding:3px 8px; font-size:10px;
background: rgba(5, 35, 70, 0.95);
color:#e8f5ff;
cursor:pointer;
}
.post-card-comments {
font-size:10px; color:#9eb7ff; padding:4px 6px;
border-radius:8px; background: rgba(3, 14, 32, 0.95);
}
.post-pin--expanded .post-card-pointer {
position:absolute; position:absolute;
left:0; top:0;
transform: translate(-50%, -100%);
width:0; height:0; width:0; height:0;
border-left: 10px solid transparent; border-left: 10px solid transparent;
border-right:10px solid transparent; border-right:10px solid transparent;
border-top: 11px solid rgba(8, 20, 40, 0.97); border-top: 11px solid rgba(8, 20, 40, 0.97);
filter: drop-shadow(0 2px 4px rgba(0,0,0,.8)); filter: drop-shadow(0 2px 4px rgba(0,0,0,.8));
} transform: translate(-50%, -100%);
/* ===== Template marker wrappers (pointers stay the same classes) ===== */
.sw-template-mini-wrap{
position: absolute;
left: 0; left: 0;
top: 0; top: 0;
transform: translate(-50%, calc(-100% - 12px));
} }
.sw-expanded-shell{ /* Simple line from marker to card */
padding: 10px 12px; .sw-overlay-line{
}
.sw-expanded-top{
display:flex;
gap:10px;
align-items:flex-start;
}
.sw-expanded-left{
flex: 1;
}
.sw-expanded-right{
width: 150px;
min-width: 140px;
display:flex;
flex-direction:column;
gap:8px;
padding-top: 4px;
}
.sw-watch-title{
font-size: 11px;
font-weight: 800;
color:#bfdbfe;
text-transform: uppercase;
letter-spacing: .06em;
}
.sw-watch-list{
display:flex;
flex-direction:column;
gap:6px;
}
.sw-watch-item{
font-size: 11px;
color:#e8f5ff;
background: rgba(3, 14, 32, 0.75);
border: 1px solid rgba(90, 190, 255, 0.35);
border-radius: 10px;
padding: 6px 8px;
}
.sw-watch-msg{
opacity:.85;
margin-top:2px;
font-size: 10px;
color:#c7e3ff;
}
.sw-add-feed-btn{
margin-top: 2px;
border: 1px solid rgba(148,163,184,.7);
border-radius: 10px;
padding: 8px 10px;
background: rgba(5, 35, 70, 0.85);
color:#e8f5ff;
font-weight: 900;
font-size: 11px;
cursor:pointer;
}
.sw-news-generated{
margin-top: 10px;
padding: 8px 10px;
border-radius: 12px;
background: rgba(3, 14, 32, 0.75);
border: 1px solid rgba(90, 190, 255, 0.35);
}
.sw-news-title{
font-size: 12px;
font-weight: 900;
color:#e8f5ff;
margin-bottom: 2px;
}
.sw-news-sub{
font-size: 10px;
color:#9eb7ff;
}
.sw-actions-row{
margin-top: 8px;
}
.sw-livechat{
margin-top: 8px;
}
.sw-livechat-title{
font-weight: 800;
letter-spacing:.04em;
margin-bottom: 6px;
}
.sw-livechat-body{
display:flex;
flex-direction:column;
gap:3px;
margin-bottom: 8px;
opacity:.95;
}
.sw-chat-line{
font-size: 10px;
color:#c7e3ff;
}
.sw-livechat-inputrow{
display:flex;
gap:8px;
align-items:center;
}
.sw-livechat-input{
flex:1;
border-radius: 999px;
border: 1px solid rgba(90, 190, 255, 0.35);
background: rgba(3, 14, 32, 0.75);
color:#e8f5ff;
padding: 8px 10px;
font-size: 11px;
outline: none;
}
.sw-livechat-post{
border:none;
border-radius: 10px;
padding: 8px 12px;
background: rgba(56,189,248,0.22);
color:#e8f5ff;
font-weight: 900;
font-size: 11px;
cursor:pointer;
}
/* ===== Template marker wrappers (pointers stay the same classes) ===== */
.sw-template-mini-wrap{
position:absolute; position:absolute;
height: 2px;
background: rgba(56,189,248,0.35);
transform-origin: 0 50%;
left: 0; left: 0;
top: 0; top: 0;
transform: translate(-50%, calc(-100% - 12px)); width: 0;
} filter: drop-shadow(0 1px 2px rgba(0,0,0,.6));
.sw-expanded-shell{
padding: 10px 12px;
}
.sw-expanded-top{
display:flex;
gap:10px;
align-items:flex-start;
}
.sw-expanded-left{
flex: 1;
}
.sw-expanded-right{
width: 150px;
min-width: 140px;
display:flex;
flex-direction:column;
gap:8px;
padding-top: 4px;
}
.sw-watch-title{
font-size: 11px;
font-weight: 800;
color:#bfdbfe;
text-transform: uppercase;
letter-spacing: .06em;
}
.sw-watch-list{
display:flex;
flex-direction:column;
gap:6px;
}
.sw-watch-item{
font-size: 11px;
color:#e8f5ff;
background: rgba(3, 14, 32, 0.75);
border: 1px solid rgba(90, 190, 255, 0.35);
border-radius: 10px;
padding: 6px 8px;
}
.sw-watch-msg{
opacity:.85;
margin-top:2px;
font-size: 10px;
color:#c7e3ff;
}
.sw-add-feed-btn{
margin-top: 2px;
border: 1px solid rgba(148,163,184,.7);
border-radius: 10px;
padding: 8px 10px;
background: rgba(5, 35, 70, 0.85);
color:#e8f5ff;
font-weight: 900;
font-size: 11px;
cursor:pointer;
}
.sw-news-generated{
margin-top: 10px;
padding: 8px 10px;
border-radius: 12px;
background: rgba(3, 14, 32, 0.75);
border: 1px solid rgba(90, 190, 255, 0.35);
}
.sw-news-title{
font-size: 12px;
font-weight: 900;
color:#e8f5ff;
margin-bottom: 2px;
}
.sw-news-sub{
font-size: 10px;
color:#9eb7ff;
}
.sw-actions-row{
margin-top: 8px;
}
.sw-livechat{
margin-top: 8px;
}
.sw-livechat-title{
font-weight: 800;
letter-spacing:.04em;
margin-bottom: 6px;
}
.sw-livechat-body{
display:flex;
flex-direction:column;
gap:3px;
margin-bottom: 8px;
opacity:.95;
}
.sw-chat-line{
font-size: 10px;
color:#c7e3ff;
}
.sw-livechat-inputrow{
display:flex;
gap:8px;
align-items:center;
}
.sw-livechat-input{
flex:1;
border-radius: 999px;
border: 1px solid rgba(90, 190, 255, 0.35);
background: rgba(3, 14, 32, 0.75);
color:#e8f5ff;
padding: 8px 10px;
font-size: 11px;
outline: none;
}
.sw-livechat-post{
border:none;
border-radius: 10px;
padding: 8px 12px;
background: rgba(56,189,248,0.22);
color:#e8f5ff;
font-weight: 900;
font-size: 11px;
cursor:pointer;
}
/* ===== Template marker wrappers (pointers stay the same classes) ===== */
.sw-template-mini-wrap{
position: absolute;
left: 0;
top: 0;
transform: translate(-50%, calc(-100% - 12px));
} }
/* ===== Expanded layout pieces (same as before) ===== */
.sw-expanded-shell{ padding: 10px 12px; } .sw-expanded-shell{ padding: 10px 12px; }
.sw-expanded-top{ .sw-expanded-top{
@ -511,156 +235,15 @@
cursor:pointer; cursor:pointer;
} }
/* ========================================================= /* Mobile: keep overlay readable */
RESPONSIVE SIZING FIX
- Mini markers were too big (280x110 real size)
- Expanded card was overflowing on mobile
========================================================= */
/* Mini template wrapper: keep pointer EXACTLY as-is, only scale the card */
.sw-template-mini-wrap{
/* was: translate(-50%, calc(-100% - 12px)) */
transform: translate(-50%, calc(-100% - 12px)) scale(var(--sw-mini-scale, 0.72));
transform-origin: bottom center;
will-change: transform;
}
/* Expanded card: make it fit phone screens nicely */
.post-pin--expanded .post-card{
max-width: min(92vw, 380px);
width: auto;
max-height: min(72vh, 640px);
overflow: auto;
-webkit-overflow-scrolling: touch;
}
/* Layout in expanded sketch area */
.sw-expanded-top{
gap: 10px;
}
/* Mobile tweaks */
@media (max-width: 640px){ @media (max-width: 640px){
/* Shrink mini markers even more on phones */ .sw-expanded-overlay-card{
.sw-template-mini-wrap{ width: min(94vw, 360px);
--sw-mini-scale: 0.58;
} }
/* Expanded: stack instead of side-by-side */
.sw-expanded-top{ .sw-expanded-top{
flex-direction: column; flex-direction: column;
} }
/* Right panel becomes horizontal strip (or you can hide it if you prefer) */
.sw-expanded-right{ .sw-expanded-right{
width: 100%; display:none;
min-width: 0;
flex-direction: row;
gap: 8px;
align-items: center;
overflow-x: auto;
padding-top: 0;
}
.sw-watch-title{
white-space: nowrap;
margin-right: 6px;
}
.sw-watch-list{
flex-direction: row;
gap: 8px;
}
.sw-watch-item{
min-width: 180px;
flex: 0 0 auto;
}
.sw-add-feed-btn{
flex: 0 0 auto;
white-space: nowrap;
}
}
/* SW_EXPANDED_OFFSET_FIX: lower expanded cards on phones (keep pointers unchanged) */
@media (max-width: 640px){
.post-pin--expanded .post-card{
transform: translate(-50%, calc(-100% - 6px));
}
}
/* SW_MOBILE_EXPANDED_TIGHTEN: make expanded marker card less tall on phones (no pointer change) */
@media (max-width: 640px){
.post-pin--expanded .post-card{
padding: 8px 10px;
max-width: 300px;
}
.post-pin--expanded .post-card-main{ gap: 6px; margin-bottom: 6px; }
.post-pin--expanded .post-card-media{ width: 56px; height: 56px; border-radius: 10px; }
.post-pin--expanded .post-card-title{ font-size: 12px; line-height: 15px; }
.post-pin--expanded .post-card-body{ font-size: 11px; max-height: 56px; }
}
/* SW_TEMPLATE_FULL_TIGHTEN: make template-full card less tall on phones */
@media (max-width: 640px){
.sw-template-full{
transform: translate(-50%, calc(-100% - 14px)) scale(0.88);
transform-origin: bottom center;
}
}
/* ===== MOBILE TUNING: expanded marker popup (template OR classic) ===== */
@media (max-width: 640px){
/* shrink the whole expanded marker a bit */
.post-pin--expanded{
transform: translate3d(0,0,0) scale(0.86);
transform-origin: bottom center;
}
/* keep it inside screen */
.post-pin--expanded .post-card{
max-width: min(92vw, 340px) !important;
}
/* title/body: prevent giant blocks */
.post-pin--expanded .post-card-title{ font-size: 12px !important; line-height: 15px !important; }
.post-pin--expanded .post-card-body{ font-size: 11px !important; line-height: 14px !important; max-height: 54px !important; }
/* buttons/footer area: tighter */
.post-pin--expanded .post-card-footer{ gap: 5px !important; }
.post-pin--expanded .post-card-btn{ font-size: 10px !important; padding: 3px 7px !important; }
}
/* === FINAL OVERRIDES (safe) === */
/* Keep pointers unchanged; only scale the mini template card */
.sw-template-mini-wrap{
transform: translate(-50%, calc(-100% - 12px)) scale(0.62) !important;
transform-origin: bottom center;
}
@media (max-width: 640px){
.sw-template-mini-wrap{
transform: translate(-50%, calc(-100% - 12px)) scale(0.52) !important;
}
}
/* === SW MOBILE: make expanded popup shorter (no pointer change) === */
@media (max-width: 640px){
.post-pin--expanded .post-card{
max-height: 58vh !important;
overflow: auto !important;
-webkit-overflow-scrolling: touch;
}
/* keep right panel from making it tall */
.sw-expanded-right{
display:none !important;
}
.sw-news-generated{
margin-top: 8px;
} }
} }

View File

@ -15,8 +15,15 @@
display:flex; flex-direction:column; gap:.5rem; display:flex; flex-direction:column; gap:.5rem;
} }
/* ✅ Sub-categories row MUST stay above the Sociowall overlap */ /* Sub-categories row MUST stay above the Sociowall overlap
.map-overlay-bottom { bottom: calc(3.2rem + var(--sw-below-overlap, 0px) + env(safe-area-inset-bottom)); left:50%; transform:translateX(-50%); display:flex; gap:.4rem; } And move DOWN by ~10px (less bottom padding) */
.map-overlay-bottom{
bottom: calc(2.6rem + var(--sw-below-overlap, 0px) + env(safe-area-inset-bottom));
left:50%;
transform:translateX(-50%);
display:flex;
gap:.4rem;
}
.map-overlay-myloc { right:.7rem; bottom:6rem; } .map-overlay-myloc { right:.7rem; bottom:6rem; }
@ -31,7 +38,7 @@
background:#38bdf8; transform:translate(-50%,-50%); background:#38bdf8; transform:translate(-50%,-50%);
} }
.crosshair-aim::before{ width:70%; height:1px; } .crosshair-aim::before{ width:70%; height:1px; }
.crosshair-aim::after{ width:1px; height:70%; } .crosshair-aim::after{ width:1px; height:70px; }
.create-post-panel { .create-post-panel {
left:50%; bottom:20%; transform:translateX(-50%); left:50%; bottom:20%; transform:translateX(-50%);

View File

@ -4,6 +4,13 @@
gap:.6rem; gap:.6rem;
align-items:stretch; align-items:stretch;
min-width:0; min-width:0;
flex-wrap: nowrap; /* ✅ keep side-by-side */
}
/* Small top nudge down (as requested) */
.sociowall-panel,
.chat-panel{
margin-top: 5px; /* ✅ descend 5px */
} }
.sociowall-panel{ .sociowall-panel{
@ -48,7 +55,7 @@
cursor:pointer; cursor:pointer;
} }
/* SCALE the fixed 280px template so it never forces width */ /* SCALE the fixed template so it never forces width */
.post-card .sw-wall-mini{ .post-card .sw-wall-mini{
--sw-wall-scale: 0.86; --sw-wall-scale: 0.86;
transform: scale(var(--sw-wall-scale)); transform: scale(var(--sw-wall-scale));
@ -62,10 +69,12 @@
.status-text{ font-size:.72rem; opacity:.85; margin:.15rem 0; } .status-text{ font-size:.72rem; opacity:.85; margin:.15rem 0; }
.status-error{ color:#fecaca; } .status-error{ color:#fecaca; }
/* ✅ Chat fixed column that can shrink a bit on narrow screens */
.chat-panel{ .chat-panel{
flex: 0 0 190px;
width: 190px; width: 190px;
min-width: 190px; min-width: 160px; /* allow shrink in portrait */
max-width: 220px; max-width: min(220px, 42vw);
min-height: 160px; min-height: 160px;
background: rgba(15, 23, 42, 0.96); background: rgba(15, 23, 42, 0.96);
border-radius: 14px; border-radius: 14px;
@ -98,9 +107,9 @@
.chat-users li{ display:flex; align-items:center; gap:.3rem; padding:.18rem .1rem; } .chat-users li{ display:flex; align-items:center; gap:.3rem; padding:.18rem .1rem; }
.chat-users li::before{ content:"👤"; font-size:.72rem; opacity:.9; } .chat-users li::before{ content:"👤"; font-size:.72rem; opacity:.9; }
/* stack only very small */ /* Stack only ultra-ultra-small */
@media (max-width: 420px){ @media (max-width: 340px){
.below-row{ flex-direction: column; } .below-row{ flex-direction: column; }
.chat-panel{ width:100%; min-width:0; max-width:none; } .chat-panel{ width:100%; min-width:0; max-width:none; flex: 1 1 auto; }
.post-list{ max-height: 46vh; } .post-list{ max-height: 46vh; }
} }