change design

This commit is contained in:
SlimusMinus
2026-08-17 23:17:30 +03:00
parent ed92e45f84
commit f02b25e8f3
24 changed files with 1199 additions and 799 deletions

View File

@@ -98,10 +98,10 @@ function LoginFormInline({ saveAuth, onClose, onSwitch }) {
<SubmitButton loading={loading} label={t('auth.login')} />
<p style={{textAlign: "center", marginTop: 14, color: "rgba(255,255,255,.45)", fontSize: ".85rem"}}>
<p style={{textAlign: "center", marginTop: 14, color: "var(--muted)", fontSize: ".85rem"}}>
{t('auth.noAccount')}{" "}
<button onClick={onSwitch}
style={{background: "none", border: "none", color: "#2de0c8", cursor: "pointer", fontWeight: 700, fontSize: ".85rem", fontFamily: "var(--font)",}}
style={{background: "none", border: "none", color: "var(--teal)", cursor: "pointer", fontWeight: 700, fontSize: ".85rem", fontFamily: "var(--font)",}}
>
{t('auth.register')}
</button>
@@ -216,10 +216,10 @@ function RegisterFormInline({ saveAuth, onClose, onSwitch }) {
<SubmitButton loading={loading} label={t('auth.createAccountBtn')} />
<p style={{textAlign: "center", marginTop: 14, color: "rgba(255,255,255,.45)", fontSize: ".85rem"}}>
<p style={{textAlign: "center", marginTop: 14, color: "var(--muted)", fontSize: ".85rem"}}>
{t('auth.hasAccount')}{" "}
<button onClick={onSwitch}
style={{background: "none", border: "none", color: "#2de0c8", cursor: "pointer", fontWeight: 700, fontSize: ".85rem", fontFamily: "var(--font)",}}>
style={{background: "none", border: "none", color: "var(--teal)", cursor: "pointer", fontWeight: 700, fontSize: ".85rem", fontFamily: "var(--font)",}}>
{t('auth.loginLink')}
</button>
</p>

View File

@@ -1,14 +1,89 @@
import { useState } from "react";
import { useTranslation } from 'react-i18next';
import ProfileAvatar from "./profile/ProfileAvatar";
export default function RightSidebar() {
function FollowButton() {
const [following, setFollowing] = useState(false);
const { t } = useTranslation();
return (
<button className="f-right-action-btn" onClick={() => setFollowing(f => !f)}>
{following ? t('rightSidebar.following') : t('rightSidebar.subscribe')}
</button>
);
}
export default function RightSidebar({ ads, profileData, isAuthenticated, onProfileClick, onLoginClick, onNavClick }) {
const { t } = useTranslation();
const suggestions = [...(ads || [])]
.sort((a, b) => (b.likeCount || 0) - (a.likeCount || 0))
.slice(0, 4);
const profileName = isAuthenticated
? `${profileData?.firstName || ""} ${profileData?.lastName || ""}`.trim() || profileData?.login
: null;
return (
<div className="f-right">
<div className="f-right-label">{t('rightSidebar.advertisement')}</div>
<div className="f-right-empty">
<div className="f-right-empty-icon">📢</div>
<div className="f-right-empty-text" dangerouslySetInnerHTML={{ __html: t('rightSidebar.emptyText') }} />
<button className="f-add-promo-btn">{t('rightSidebar.addAd')}</button>
<div className="f-right-profile">
{isAuthenticated ? (
<>
<ProfileAvatar avatar={profileData?.avatar} name={profileName} />
<div className="f-right-profile-info">
<div className="f-right-profile-name">{profileName}</div>
<div className="f-right-profile-sub">{profileData?.login}</div>
</div>
<button className="f-right-action-btn" onClick={onProfileClick}>
{t('rightSidebar.yourProfile')}
</button>
</>
) : (
<>
<div className="f-ig-ring">
<div className="f-ig-avatar">👤</div>
</div>
<div className="f-right-profile-info">
<div className="f-right-profile-name">{t('rightSidebar.guest')}</div>
<div className="f-right-profile-sub">{t('rightSidebar.guestSub')}</div>
</div>
<button className="f-right-action-btn" onClick={onLoginClick}>
{t('feed.login')}
</button>
</>
)}
</div>
<div className="f-right-header">
<span className="f-right-label">{t('rightSidebar.suggestions')}</span>
<button className="f-right-link" onClick={() => onNavClick?.("home")}>
{t('rightSidebar.all')}
</button>
</div>
{suggestions.length === 0 && (
<div className="f-right-suggest-sub">{t('rightSidebar.empty')}</div>
)}
{suggestions.map(ad => (
<div className="f-right-suggest" key={ad.id}>
<div className="f-ig-ring f-ig-ring-sm">
<div className="f-ig-avatar">{ad.emoji || "📋"}</div>
</div>
<div className="f-right-suggest-info">
<div className="f-right-suggest-name">{ad.title}</div>
<div className="f-right-suggest-sub">
{ad.city || (ad.type === "loss" ? t('postCard.loss') : t('postCard.found'))}
</div>
</div>
<FollowButton />
</div>
))}
<div className="f-right-foot">
<span>© {new Date().getFullYear()} {t('app.name')}</span>
<span>{t('rightSidebar.footRules')}</span>
<span>{t('rightSidebar.footPrivacy')}</span>
<span>{t('rightSidebar.footSupport')}</span>
</div>
</div>
);

View File

@@ -11,6 +11,7 @@ import { useUnread } from "../context/UnreadContext";
import { useLanguageToggle } from "../hooks/useLanguageToggle";
import { useTranslation } from 'react-i18next';
import SupportModal from "./SupportModal";
import ProfileAvatar from "./profile/ProfileAvatar";
export default function Sidebar({
open,
@@ -21,6 +22,9 @@ export default function Sidebar({
onLoginClick,
onRulesClick,
onReviewsClick,
onCreatePost,
onProfileClick,
profileData,
}) {
const navigate = useNavigate();
const { theme, toggle: toggleTheme } = useTheme();
@@ -28,6 +32,35 @@ export default function Sidebar({
const { t } = useTranslation();
const { language, toggleLanguage } = useLanguageToggle();
const [supportOpen, setSupportOpen] = useState(false);
const HelpIcon = NavIcons.help;
const handleNavItem = (itemKey) => {
if (!isAuthenticated && itemKey === "myads") {
onLoginClick();
return;
}
if (itemKey === "chat") {
if (!isAuthenticated) {
onLoginClick();
} else {
navigate("/chats");
}
return;
}
if (itemKey === "create") {
onCreatePost?.();
return;
}
onNavClick(itemKey);
};
const handleProfileClick = () => {
if (!isAuthenticated) {
onLoginClick();
} else {
onProfileClick?.();
}
};
return (
<>
@@ -40,27 +73,15 @@ export default function Sidebar({
<nav className="f-sb-nav">
{NAV_ITEMS.map(({ icon, key: itemKey, labelKey }) => {
const Ic = NavIcons[icon];
const handleClick = () => {
// «Мои объявления» требует авторизации
if (!isAuthenticated && itemKey === "myads") {
onLoginClick();
} else if (itemKey === "chat") {
if (!isAuthenticated) {
onLoginClick();
} else {
navigate("/chats");
}
} else {
onNavClick(itemKey);
}
onClose();
};
return (
<button
type="button"
key={itemKey}
className={`f-sb-item${activeNav === itemKey ? " active" : ""}`}
onClick={handleClick}
className={`f-sb-item${activeNav === itemKey ? " active" : ""}${itemKey === "create" ? " f-sb-create" : ""}`}
onClick={() => {
handleNavItem(itemKey);
onClose();
}}
>
<Ic /> {t(labelKey)}
{itemKey === "chat" && totalUnread > 0 && (
@@ -99,17 +120,37 @@ export default function Sidebar({
);
})}
<button type="button" className="f-sb-item" onClick={() => setSupportOpen(true)}>
<NavIcons.help /> {t('nav.support')}
</button>
<HelpIcon /> {t('nav.support')} </button>
<div className="f-sb-divider" />
<button type="button" className="f-sb-item f-sb-theme-toggle" onClick={toggleTheme}>
<button type="button" className="f-sb-item" onClick={toggleTheme}>
{theme === "dark" ? <SunIcon /> : <MoonIcon />}
{theme === "dark" ? t('sidebar.lightTheme') : t('sidebar.darkTheme')}
</button>
<button type="button" className="f-sb-item f-sb-lang-toggle" onClick={toggleLanguage}>
<button type="button" className="f-sb-item" onClick={toggleLanguage}>
<GlobeIcon />
{language === 'ru' ? 'English' : 'Русский'}
</button>
<div className="f-sb-divider" />
<button type="button" className="f-sb-profile" onClick={handleProfileClick}>
{isAuthenticated ? (
<ProfileAvatar
avatar={profileData?.avatar}
name={`${profileData?.firstName || ""} ${profileData?.lastName || ""}`}
/>
) : (
<div className="f-profile-avatar-placeholder">👤</div>
)}
<div className="f-sb-profile-info">
<div className="f-sb-profile-name">
{isAuthenticated
? `${profileData?.firstName || ""} ${profileData?.lastName || ""}`.trim() || profileData?.login
: t('feed.login')}
</div>
<div className="f-sb-profile-login">
{isAuthenticated ? profileData?.login || t('profile.default') : t('auth.noAccount')}
</div>
</div>
</button>
</div>
</div>
{supportOpen && <SupportModal onClose={() => setSupportOpen(false)} />}

View File

@@ -2,6 +2,7 @@ import { useRef } from "react";
import Picker from "@emoji-mart/react";
import emojiData from "@emoji-mart/data";
import { useClickOutside } from "../../hooks/useClickOutside";
import { useTheme } from "../../context/ThemeContext";
export default function ChatInputBar({
input,
@@ -16,6 +17,7 @@ export default function ChatInputBar({
}) {
const emojiPickerRef = useRef(null);
useClickOutside(emojiPickerRef, onCloseEmoji, showEmoji);
const { theme } = useTheme();
return (
<div className="chat-input-area">
@@ -24,7 +26,7 @@ export default function ChatInputBar({
<Picker
data={emojiData}
onEmojiSelect={onEmojiSelect}
theme="dark"
theme={theme}
previewPosition="none"
skinTonePosition="none"
searchPosition="sticky"

View File

@@ -1,28 +1,10 @@
import AppTopBar from "../layout/AppTopBar";
export default function FeedTopBar({
onMenuClick,
onCreatePost,
isAuthenticated,
profileData,
onProfileClick,
onLoginClick,
t,
}) {
export default function FeedTopBar({ onMenuClick }) {
return (
<AppTopBar
onMenuClick={onMenuClick}
isAuthenticated={isAuthenticated}
profileData={profileData}
onProfileClick={onProfileClick}
onLoginClick={onLoginClick}
loginLabel={t('feed.login')}
centerContent={
<button className="f-post-btn" onClick={onCreatePost}>
<span style={{ fontSize: "0.9rem", fontWeight: 900, lineHeight: 1 }}>+</span>
{t('feed.createPost')}
</button>
}
hideProfile
/>
);
}

View File

@@ -51,4 +51,11 @@ export const NavIcons = {
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
),
plus: () => (
<svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.4} strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="18" height="18" rx="6" />
<line x1="12" y1="8" x2="12" y2="16" />
<line x1="8" y1="12" x2="16" y2="12" />
</svg>
),
};

View File

@@ -1,9 +1,10 @@
import MenuIcon from "../icons/MenuIcon";
import ProfileAvatar from "../profile/ProfileAvatar";
import { useTranslation } from 'react-i18next';
/**
* Общая верхняя панель приложения: бургер-меню слева,
* произвольный контент по центру, профиль/логин справа.
* Общая верхняя панель приложения (мобильная версия Instagram-style):
* бургер-меню слева, название сервиса, контент по центру, профиль/логин справа.
*/
export default function AppTopBar({
onMenuClick,
@@ -13,14 +14,19 @@ export default function AppTopBar({
onProfileClick,
onLoginClick,
loginLabel,
hideProfile = false,
}) {
const { t } = useTranslation();
return (
<div className="f-topbar">
<button className="f-menu-btn" onClick={onMenuClick}>
<MenuIcon />
</button>
<span className="f-topbar-wordmark">{t('app.name')}</span>
<div className="f-topbar-center">{centerContent}</div>
{isAuthenticated ? (
{hideProfile ? (
<span className="f-topbar-spacer" />
) : isAuthenticated ? (
<div className="f-topbar-profile" onClick={onProfileClick}>
<ProfileAvatar
avatar={profileData.avatar}

View File

@@ -1,9 +1,10 @@
export const NAV_ITEMS = [
{ icon: "grid", key: "home", labelKey: "nav.home" },
{ icon: "grid", key: "home", labelKey: "nav.home" },
{ icon: "chat", key: "chat", labelKey: "nav.chat" },
{ icon: "box", key: "items", labelKey: "nav.items" },
{ icon: "paw", key: "pets", labelKey: "nav.pets" },
{ icon: "file", key: "myads", labelKey: "nav.myAds" },
{ icon: "box", key: "items", labelKey: "nav.items" },
{ icon: "paw", key: "pets", labelKey: "nav.pets" },
{ icon: "chat", key: "chat", labelKey: "nav.chat" },
{ icon: "plus", key: "create", labelKey: "nav.create" },
];
export const NAV_BOTTOM = [

View File

@@ -7,9 +7,9 @@ const STORAGE_KEY = "theme";
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState(() => {
try {
return localStorage.getItem(STORAGE_KEY) || "dark";
return localStorage.getItem(STORAGE_KEY) || "light";
} catch {
return "dark";
return "light";
}
});

View File

@@ -8,6 +8,10 @@
"items": "Items",
"pets": "Pets",
"chat": "Chat",
"create": "Create",
"search": "Search",
"map": "Map",
"profile": "Profile",
"reviews": "Reviews",
"rules": "Platform Rules",
"support": "Support"
@@ -126,9 +130,9 @@
"createPost": "Create ad",
"login": "Log in",
"searchPlaceholder": "Search by title or description...",
"filterLoss": "🔴 Lost",
"filterLoss": "Lost",
"filterAll": "All ads",
"filterFound": "🟢 Found",
"filterFound": "Found",
"filterCitiesPlaceholder": "Select city…",
"filterCitiesEmpty": "No cities found",
"filterCitiesAdd": "Add \"{{city}}\"",
@@ -149,7 +153,8 @@
"itemFound": "✓ Item found",
"showOnMap": "Show on map — {{address}}",
"contactError": "Unable to open the chat. Please try again",
"reward": "Reward"
"reward": "Reward",
"likes": "Likes: {{count}}"
},
"chat": {
"title": "Chat",
@@ -188,7 +193,18 @@
"rightSidebar": {
"advertisement": "Advertisement",
"emptyText": "Your ad<br />could be here",
"addAd": "+ Add advertisement"
"addAd": "+ Add advertisement",
"suggestions": "Suggestions for you",
"all": "See all",
"subscribe": "Follow",
"following": "Following",
"yourProfile": "Your profile",
"guest": "Guest",
"guestSub": "Log in to open your profile",
"empty": "No ads yet",
"footRules": "Rules",
"footPrivacy": "Privacy",
"footSupport": "Support"
},
"landing": {
"eyebrow": "Service for finding lost pets and items",

View File

@@ -8,6 +8,10 @@
"items": "Вещи",
"pets": "Питомцы",
"chat": "Чат",
"create": "Создать",
"search": "Поиск",
"map": "Карта",
"profile": "Профиль",
"reviews": "Отзывы",
"rules": "Правила площадки",
"support": "Поддержка"
@@ -127,9 +131,9 @@
"createPost": "Разместить объявление",
"login": "Войти",
"searchPlaceholder": "Поиск по названию или описанию...",
"filterLoss": "🔴 Потери",
"filterLoss": "Потери",
"filterAll": "Все объявления",
"filterFound": "🟢 Находки",
"filterFound": "Находки",
"filterCitiesPlaceholder": "Выберите город…",
"filterCitiesEmpty": "Города не найдены",
"filterCitiesAdd": "Добавить «{{city}}»",
@@ -150,7 +154,8 @@
"itemFound": "✓ Вещь найдена",
"showOnMap": "Показать на карте — {{address}}",
"contactError": "Не удалось открыть чат. Попробуйте ещё раз",
"reward": "Вознаграждение"
"reward": "Вознаграждение",
"likes": "Нравится: {{count}}"
},
"chat": {
"title": "Чат",
@@ -189,7 +194,18 @@
"rightSidebar": {
"advertisement": "Реклама",
"emptyText": "Здесь будет<br />ваша реклама",
"addAd": "+ Добавить рекламу"
"addAd": "+ Добавить рекламу",
"suggestions": "Рекомендации для вас",
"all": "Все",
"subscribe": "Подписаться",
"following": "Подписано",
"yourProfile": "Ваш профиль",
"guest": "Гость",
"guestSub": "Войдите, чтобы открыть профиль",
"empty": "Объявлений пока нет",
"footRules": "Правила",
"footPrivacy": "Конфиденциальность",
"footSupport": "Поддержка"
},
"landing": {
"eyebrow": "Сервис поиска пропавших питомцев и вещей",

View File

@@ -1,8 +1,9 @@
import { useState, useEffect, useCallback, useMemo } from "react";
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import "../styles/index.css";
import { getImageUrl } from "../config/authConstants";
import Sidebar from "../components/Sidebar";
import BottomNav from "../components/nav/BottomNav";
import RightSidebar from "../components/RightSidebar";
import CreateUpdatePost from "./CreateUpdatePost";
import ImageViewer from "../components/ImageViewer";
@@ -45,6 +46,11 @@ export default function MainFeed() {
const [searchQuery, setSearchQuery] = useState("");
const [liked, setLiked] = useState(new Set());
const feedRef = useRef(null);
const isSearchView = new URLSearchParams(location.search).get("view") === "search";
const hasActiveFilters = searchQuery.trim().length > 0 || selectedCities.length > 0;
const feed = useFeedPosts();
const myPosts = useMyPosts(user?.id);
const cityFeed = useCityFeed(selectedCities);
@@ -135,6 +141,28 @@ export default function MainFeed() {
else modals.open("edit", ad);
}, [isAuthenticated, modals]);
const handleHomeClick = useCallback(() => {
setActiveNav("home");
if (isSearchView) navigate("/feed");
feedRef.current?.scrollTo({ top: 0, behavior: "smooth" });
}, [isSearchView, navigate]);
const handleSearchClick = useCallback(() => {
setActiveNav("home");
setSearchQuery("");
setSelectedCities([]);
if (!isSearchView) navigate("/feed?view=search");
feedRef.current?.scrollTo({ top: 0, behavior: "smooth" });
}, [isSearchView, navigate]);
// Карта с объявлениями — иконка добавлена, функционал ещё не реализован
const handleMapClick = useCallback(() => {}, []);
const handleProfileClick = useCallback(() => {
if (!isAuthenticated) modals.open("auth");
else modals.open("profile");
}, [isAuthenticated, modals]);
const filteredAds = useMemo(() => {
let result;
if (activeNav === "myads") {
@@ -161,7 +189,7 @@ export default function MainFeed() {
const listError = activeNav === "myads" ? myPosts.error : feed.error;
return (
<div className="f-app">
<div className={`f-app${isSearchView ? " f-search-view" : ""}`}>
<Sidebar
open={sidebarOpen}
activeNav={activeNav}
@@ -171,31 +199,28 @@ export default function MainFeed() {
onLoginClick={() => modals.open("auth")}
onRulesClick={() => modals.open("rules")}
onReviewsClick={() => navigate("/reviews")}
onCreatePost={handleCreatePost}
onProfileClick={() => modals.open("profile")}
profileData={profileData}
/>
<div className="f-main">
<FeedTopBar
onMenuClick={() => setSidebarOpen(v => !v)}
onCreatePost={handleCreatePost}
isAuthenticated={isAuthenticated}
profileData={profileData}
onProfileClick={() => modals.open("profile")}
onLoginClick={() => modals.open("auth")}
t={t}
/>
<div className="f-main f-main-feed">
<FeedTopBar onMenuClick={() => setSidebarOpen(v => !v)} />
<FeedSearchBar value={searchQuery} onChange={setSearchQuery} t={t} />
<div className="f-search-panel">
<FeedSearchBar value={searchQuery} onChange={setSearchQuery} t={t} />
{activeNav === "home" && (
<div className="f-city-filter-wrap">
<CityFilter cities={cities} value={selectedCities} onChange={setSelectedCities} />
</div>
)}
{activeNav === "home" && (
<div className="f-city-filter-wrap">
<CityFilter cities={cities} value={selectedCities} onChange={setSelectedCities} />
</div>
)}
</div>
<TypeFilterBar value={typeFilter} onChange={setTypeFilter} t={t} />
<div className="f-wrap">
<div className="f-feed">
<div className="f-feed" ref={feedRef}>
<FeedContent
loading={listLoading}
cityLoading={cityFeed.cityLoading}
@@ -221,7 +246,14 @@ export default function MainFeed() {
</div>
)}
</div>
<RightSidebar />
<RightSidebar
ads={filteredAds}
profileData={profileData}
isAuthenticated={isAuthenticated}
onProfileClick={() => modals.open("profile")}
onLoginClick={() => modals.open("auth")}
onNavClick={setActiveNav}
/>
</div>
</div>
@@ -253,6 +285,18 @@ export default function MainFeed() {
postData={modals.modal.payload.ad}
/>
)}
<BottomNav
activeTab={isSearchView ? "search" : "home"}
hasFilters={hasActiveFilters}
profileData={profileData}
onHome={handleHomeClick}
onMap={handleMapClick}
onCreatePost={handleCreatePost}
onSearch={handleSearchClick}
onProfile={handleProfileClick}
t={t}
/>
</div>
);
}

View File

@@ -1,4 +1,4 @@
import {useRef, useState} from "react";
import {useRef, useState, useEffect} from "react";
import PinIcon from "../components/icons/PinIcon";
import CalendarIcon from "../components/icons/CalendarIcon";
import PhoneIcon from "../components/icons/PhoneIcon";
@@ -14,10 +14,31 @@ import {useTranslation} from 'react-i18next';
export default function PostOnFeed({ ad, liked, onLike, onEdit, onDelete, onMarkFound, isOwner, onImageClick, onAuthRequired }) {
const { t } = useTranslation();
const [showCalendar, setShowCalendar] = useState(false);
const [titleExpanded, setTitleExpanded] = useState(false);
const [descExpanded, setDescExpanded] = useState(false);
const [titleTruncated, setTitleTruncated] = useState(false);
const [descTruncated, setDescTruncated] = useState(false);
const dateAnchorRef = useRef(null);
const titleRef = useRef(null);
const descRef = useRef(null);
const { isAuthenticated } = useAuthContext();
const { contactSeller, error: contactError, clearError } = useContactSeller(ad);
// Определяем, обрезан ли title/description (тремя точками), и реагируем на ресайз
useEffect(() => {
const check = () => {
if (!titleExpanded && titleRef.current) {
setTitleTruncated(titleRef.current.scrollWidth > titleRef.current.clientWidth + 1);
}
if (!descExpanded && descRef.current) {
setDescTruncated(descRef.current.scrollHeight > descRef.current.clientHeight + 1);
}
};
check();
window.addEventListener("resize", check);
return () => window.removeEventListener("resize", check);
}, [titleExpanded, descExpanded]);
const handleContact = (e) => {
e.stopPropagation();
if (!isAuthenticated) {
@@ -35,131 +56,137 @@ export default function PostOnFeed({ ad, liked, onLike, onEdit, onDelete, onMark
const isClosed = ad.status === 'CLOSED';
const roleClass = ad.userRole === 'LOST_AND_FOUND' ? 'f-card-role-lost-found'
: ad.userRole === 'METRO' ? 'f-card-role-metro' : '';
const roleBadge = ad.userRole === 'LOST_AND_FOUND' ? (
<div className="f-role-badge f-role-lost-found" title={t('postCard.lostFoundBureau')}>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"/>
<path d="M12 6v6l4 2"/>
</svg>
{t('postCard.lostFoundBureau')}
const typeBadge = (
<div className={`f-type-badge ${ad.type === "loss" ? "f-type-loss" : "f-type-found"}`}>
{ad.type === "loss" ? t('postCard.loss') : t('postCard.found')}
</div>
) : ad.userRole === 'METRO' ? (
<div className="f-role-badge f-role-metro" title={t('postCard.metro')}>
<span className="f-role-metro-letter">М</span>
{t('postCard.metro')}
</div>
) : null;
);
return (
<div className={`f-card${isClosed ? ' f-card-closed' : ''}${roleClass ? ' ' + roleClass : ''}`}>
<div className={`f-card${isClosed ? ' f-card-closed' : ''}`}>
<div className="f-card-inner">
<div className="f-card-img" onClick={e => { e.stopPropagation(); if (onImageClick) onImageClick(ad.images, 0, ad); }}>
{ad.reward && (
<div className="f-card-reward">
💰 {t("postCard.reward")}
</div>
)}
{imageUrl
? <img src={imageUrl} alt={ad.title} />
: <span className="f-card-emoji">{ad.emoji || "📋"}</span>
}
</div>
<div className="f-card-body">
<div className="f-card-badges">
{ad.category && <div className="f-cat-badge">{ad.category}</div>}
<div className={`f-type-badge ${ad.type === "loss" ? "f-type-loss"
: "f-type-found"}`}>
{ad.type === "loss" ? t('postCard.loss') : t('postCard.found')}
</div>
{roleBadge}
</div>
<div className="f-card-title">
<div className="f-card-head">
<div className="f-card-author">
<div
ref={titleRef}
className={`f-card-author-name${titleTruncated ? " f-truncatable" : ""}${titleExpanded ? " f-expanded" : ""}`}
onClick={titleTruncated ? () => setTitleExpanded(v => !v) : undefined}
>
{ad.title}
{isClosed && <span className="f-status-badge f-status-closed">{t(
'postCard.foundStatus')}</span>}
</div>
<div className="f-card-desc">{ad.description}</div>
<div className="f-card-meta">
{address && <div className="f-meta-pill"><PinIcon/> {address}
</div>}
{createdAt && (
<div
className="f-meta-pill f-date-pill"
ref={dateAnchorRef}
onClick={e => {
e.stopPropagation();
setShowCalendar(s => !s);
}}
>
<CalendarIcon/> {createdAt}
{showCalendar && (
<DateCalendarPopup
date={ad.createdAt}
onClose={() => setShowCalendar(false)}
anchorRef={dateAnchorRef}
/>
)}
</div>
)}
</div>
<div className="f-card-foot">
<div className="f-card-foot-left">
{ad.phone && (
<a className="f-card-phone" href={`tel:${ad.phone}`}
onClick={e => e.stopPropagation()}>
<PhoneIcon/> {ad.phone}
</a>
)}
</div>
<div className="f-card-foot-right">
{!isOwner && !isClosed && (
<button type="button" className="f-card-contact"
onClick={handleContact}>
{t('postCard.write')}
</button>
)}
{onEdit && isOwner && !isClosed && (
<button type="button" className="f-card-edit-btn"
onClick={e => {
e.stopPropagation();
onEdit(ad);
}}>
✏️
</button>
)}
{onDelete && isOwner && (
<button type="button" className="f-card-delete-btn"
onClick={e => {
e.stopPropagation();
onDelete(ad.id);
}}>
<TrashIcon/>
</button>
)}
{onMarkFound && isOwner && !isClosed && (
<button type="button" className="f-card-found-btn"
onClick={e => {
e.stopPropagation();
onMarkFound(ad.id);
}}>
{t('postCard.itemFound')}
</button>
)}
<button type="button"
className={`f-like-btn${liked ? " liked" : ""}`}
</div>
<div className="f-card-head-side">
{typeBadge}
{isClosed && (
<span className="f-status-badge f-status-closed">{t('postCard.foundStatus')}</span>
)}
</div>
</div>
<div className="f-card-img" onClick={e => { e.stopPropagation(); if (onImageClick) onImageClick(ad.images, 0, ad); }}>
{imageUrl
? <img src={imageUrl} alt={ad.title} />
: <span className="f-card-emoji">{ad.emoji || "📋"}</span>
}
</div>
<div className="f-card-actions">
<div className="f-card-actions-left">
<button type="button"
className={`f-card-icon-btn${liked ? " liked" : ""}`}
onClick={e => {
e.stopPropagation();
onLike(ad.id);
}}>
<HeartIcon filled={liked}/>
</button>
<span className="f-card-like-count">{ad.likeCount || 0}</span>
</div>
<div className="f-card-actions-right">
{onEdit && isOwner && !isClosed && (
<button type="button" className="f-card-edit-btn"
onClick={e => {
e.stopPropagation();
onLike(ad.id);
onEdit(ad);
}}>
<HeartIcon filled={liked}/>
{ad.likeCount || 0}
✏️
</button>
)}
{onDelete && isOwner && (
<button type="button" className="f-card-delete-btn"
onClick={e => {
e.stopPropagation();
onDelete(ad.id);
}}>
<TrashIcon/>
</button>
)}
{ad.reward && (
<span className="f-card-reward-text">
{t("postCard.reward")}
</span>
)}
</div>
</div>
<div className="f-card-caption">
<span
ref={descRef}
className={`f-card-caption-text${descTruncated ? " f-truncatable" : ""}${descExpanded ? " f-expanded" : ""}`}
onClick={descTruncated ? () => setDescExpanded(v => !v) : undefined}
>
{ad.description}
</span>
</div>
<div className="f-card-meta">
{address && <div className="f-meta-pill"><PinIcon/> {address}</div>}
{createdAt && (
<div
className="f-meta-pill f-date-pill"
ref={dateAnchorRef}
onClick={e => {
e.stopPropagation();
setShowCalendar(s => !s);
}}
>
<CalendarIcon/> {createdAt}
{showCalendar && (
<DateCalendarPopup
date={ad.createdAt}
onClose={() => setShowCalendar(false)}
anchorRef={dateAnchorRef}
/>
)}
</div>
</div>
)}
</div>
<div className="f-card-foot">
<div className="f-card-foot-left">
{ad.phone && (
<a className="f-card-phone" href={`tel:${ad.phone}`}
onClick={e => e.stopPropagation()}>
<PhoneIcon/> {ad.phone}
</a>
)}
</div>
<div className="f-card-foot-right">
{!isOwner && !isClosed && (
<button type="button" className="f-card-contact"
onClick={handleContact}>
{t('postCard.write')}
</button>
)}
{onMarkFound && isOwner && !isClosed && (
<button type="button" className="f-card-found-btn"
onClick={e => {
e.stopPropagation();
onMarkFound(ad.id);
}}>
{t('postCard.itemFound')}
</button>
)}
</div>
</div>

View File

@@ -60,6 +60,9 @@ export default function ReviewsPage() {
isAuthenticated={isAuthenticated}
onLoginClick={() => setAuthModalOpen(true)}
onRulesClick={() => setRulesModalOpen(true)}
onCreatePost={() => navigate("/feed")}
onProfileClick={() => setProfileModalOpen(true)}
profileData={profileData}
/>
<div className="f-main">

View File

@@ -2,8 +2,8 @@
.nk-page {
font-family: 'Inter', sans-serif;
background: #0A1F1E;
color: #F4F7F6;
background: linear-gradient(180deg, #ffffff 0%, #fafafa 100%);
color: #262626;
min-height: 100vh;
}
@@ -71,10 +71,10 @@
.nk-grid {
position: absolute;
inset: 0;
background-image: radial-gradient(rgba(244, 247, 246, 0.10) 1px, transparent 1px);
background-image: radial-gradient(rgba(38, 38, 38, 0.10) 1px, transparent 1px);
background-size: 26px 26px;
animation: drift 14s linear infinite;
opacity: 0.6;
opacity: 0.5;
}
.nk-glow {
@@ -83,7 +83,7 @@
right: -10%;
width: 600px;
height: 600px;
background: radial-gradient(circle, rgba(245, 166, 35, 0.18) 0%, transparent 70%);
background: radial-gradient(circle, rgba(0, 149, 246, 0.14) 0%, transparent 70%);
filter: blur(10px);
}
@@ -100,6 +100,7 @@
font-size: 22px;
font-weight: 600;
letter-spacing: 0.01em;
color: #262626;
}
.nk-nav-right {
@@ -109,9 +110,9 @@
}
.nk-lang-btn {
background: rgba(244, 247, 246, 0.06);
border: 1px solid rgba(244, 247, 246, 0.12);
color: #F4F7F6;
background: #ffffff;
border: 1px solid #dbdbdb;
color: #262626;
padding: 9px 12px;
border-radius: 999px;
font-size: 12px;
@@ -120,23 +121,26 @@
display: flex;
align-items: center;
gap: 4px;
backdrop-filter: blur(6px);
transition: background 0.2s;
}
.nk-lang-btn:hover {
background: rgba(244, 247, 246, 0.12);
background: #f0f0f0;
}
.nk-nav-btn {
background: rgba(244, 247, 246, 0.08);
border: 1px solid rgba(244, 247, 246, 0.16);
color: #F4F7F6;
background: var(--teal, #0095f6);
border: 1px solid var(--teal, #0095f6);
color: #fff;
padding: 9px 20px;
border-radius: 999px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
backdrop-filter: blur(6px);
transition: background 0.2s;
}
.nk-nav-btn:hover {
background: #0074d9;
}
.nk-radar {
@@ -156,7 +160,7 @@
width: 140px;
height: 140px;
border-radius: 50%;
border: 1px solid rgba(245, 166, 35, 0.55);
border: 1px solid rgba(0, 149, 246, 0.5);
animation: beaconPulse 3.9s ease-out infinite;
will-change: transform;
transform: translateZ(0);
@@ -166,14 +170,14 @@
width: 14px;
height: 14px;
border-radius: 50%;
background: #F5A623;
box-shadow: 0 0 22px 6px rgba(245, 166, 35, 0.55);
background: #0095f6;
box-shadow: 0 0 22px 6px rgba(0, 149, 246, 0.5);
}
.nk-marker {
position: absolute;
color: #0A1F1E;
background: #F5A623;
color: #fff;
background: #0095f6;
width: 34px;
height: 34px;
border-radius: 50%;
@@ -181,7 +185,7 @@
align-items: center;
justify-content: center;
animation: markerIn 3.2s ease-in-out infinite;
box-shadow: 0 4px 14px rgba(245, 166, 35, 0.4);
box-shadow: 0 4px 14px rgba(0, 149, 246, 0.4);
will-change: transform;
transform: translateZ(0);
}
@@ -197,10 +201,11 @@
.nk-eyebrow {
display: inline-block;
font-size: 13px;
color: #8FADA8;
color: #737373;
letter-spacing: 0.04em;
margin-bottom: 18px;
text-transform: uppercase;
font-weight: 600;
}
.nk-h1 {
@@ -209,16 +214,20 @@
font-size: clamp(34px, 5.2vw, 58px);
line-height: 1.08;
margin: 0 0 22px;
color: #262626;
}
.nk-accent {
color: #F5A623;
background: linear-gradient(45deg, #feda75, #fa7e1e, #d62976, #962fbf, #4f5bd5);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.nk-lead {
font-size: 17px;
line-height: 1.6;
color: #C7DAD6;
color: #737373;
max-width: 500px;
margin-bottom: 34px;
}
@@ -231,28 +240,28 @@
}
.nk-cta-primary {
background: #F5A623;
color: #0A1F1E;
background: #0095f6;
color: #fff;
border: none;
padding: 15px 30px;
border-radius: 999px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
transition: transform 0.2s, box-shadow 0.2s, background 0.2s;
}
.nk-cta-primary:hover {
transform: translateY(-2px);
box-shadow: 0 12px 28px rgba(245, 166, 35, 0.35);
background: #0074d9;
box-shadow: 0 12px 28px rgba(0, 149, 246, 0.35);
}
.nk-cta-hint {
font-size: 13px;
color: #7FA39D;
color: #737373;
}
.nk-steps-section {
padding: 0 6vw 60px;
}
@@ -263,6 +272,7 @@
font-weight: 600;
margin-bottom: 42px;
text-align: center;
color: #262626;
}
.nk-steps-grid {
@@ -274,21 +284,22 @@
}
.nk-step-card {
background: rgba(244, 247, 246, 0.04);
border: 1px solid rgba(244, 247, 246, 0.10);
background: #ffffff;
border: 1px solid #dbdbdb;
border-radius: 20px;
padding: 26px 24px;
transition: border-color 0.2s;
transition: border-color 0.2s, box-shadow 0.2s;
}
.nk-step-card:hover {
border-color: rgba(244, 247, 246, 0.22);
border-color: rgba(0, 149, 246, 0.4);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
}
.nk-step-num {
font-family: 'Fraunces', serif;
font-size: 15px;
color: #F5A623;
color: #0095f6;
font-weight: 600;
}
@@ -296,12 +307,13 @@
font-size: 18px;
font-weight: 600;
margin: 12px 0 8px;
color: #262626;
}
.nk-step-text {
font-size: 14.5px;
line-height: 1.55;
color: #9FBAB5;
color: #737373;
}
.nk-final-cta {
@@ -315,7 +327,7 @@
text-align: center;
padding: 24px 0 34px;
font-size: 13px;
color: #5F827D;
color: #b0b0b0;
}
@media (max-width: 768px) {

View File

@@ -3,48 +3,48 @@
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--teal: #2de0c8;
--teal-rgb: 45, 224, 200;
--teal2: #1fc9b0;
--border: rgba(255,255,255,0.09);
--muted: rgba(255,255,255,0.38);
--muted2: rgba(255,255,255,0.15);
--teal: #0095f6;
--teal-rgb: 0, 149, 246;
--teal2: #0074d9;
--border: #dbdbdb;
--muted: #737373;
--muted2: #c7c7c7;
--font: 'Nunito', sans-serif;
--err: #ff6b6b;
--err: #ed4956;
--bg-app: linear-gradient(135deg, #1a2340 0%, #0d1018 100%);
--bg-card: linear-gradient(155deg, #1d2436, #131720);
--bg-card-popup: linear-gradient(155deg, #2b3552, #1e2742);
--bg-surface: rgba(13,16,24,.7);
--bg-surface-solid: #131720;
--bg-filter: rgba(13,16,24,.4);
--bg-card-img: linear-gradient(135deg, #1a3a4a 0%, #0f2235 100%);
--bg-input: rgba(255,255,255,.055);
--bg-overlay: rgba(0,0,0,.65);
--text-primary: #fff;
--hover-bg: rgba(255,255,255,.08);
}
[data-theme="light"] {
--teal: #1a9e8a;
--teal-rgb: 26, 158, 138;
--teal2: #148d7a;
--border: rgba(0,0,0,0.1);
--muted: rgba(0,0,0,0.45);
--muted2: rgba(0,0,0,0.18);
--err: #d32f2f;
--bg-app: linear-gradient(135deg, #f0f2f5 0%, #e2e6ed 100%);
--bg-app: #fafafa;
--bg-card: #ffffff;
--bg-card-popup: #ffffff;
--bg-surface: rgba(255,255,255,.8);
--bg-surface: #ffffff;
--bg-surface-solid: #ffffff;
--bg-filter: rgba(255,255,255,.6);
--bg-card-img: linear-gradient(135deg, #e8ecf2 0%, #d6dce6 100%);
--bg-input: rgba(0,0,0,.04);
--bg-overlay: rgba(0,0,0,.35);
--text-primary: #111;
--hover-bg: rgba(0,0,0,.06);
--bg-filter: rgba(255,255,255,.8);
--bg-card-img: linear-gradient(135deg, #feda75 0%, #fa7e1e 35%, #d62976 65%, #962fbf 100%);
--bg-input: rgba(0,0,0,.045);
--bg-overlay: rgba(0,0,0,.5);
--text-primary: #262626;
--hover-bg: rgba(0,0,0,.05);
}
[data-theme="dark"] {
--teal: #0095f6;
--teal-rgb: 0, 149, 246;
--teal2: #0074d9;
--border: #262626;
--muted: #a8a8a8;
--muted2: #4b4b4b;
--err: #ed4956;
--bg-app: #000000;
--bg-card: #1b1b1b;
--bg-card-popup: #262626;
--bg-surface: #000000;
--bg-surface-solid: #000000;
--bg-filter: rgba(0,0,0,.6);
--bg-card-img: linear-gradient(135deg, #5a3d00 0%, #6b3a10 35%, #4d1f38 65%, #3a205e 100%);
--bg-input: rgba(255,255,255,.08);
--bg-overlay: rgba(0,0,0,.65);
--text-primary: #f5f5f5;
--hover-bg: rgba(255,255,255,.08);
}
@keyframes shimmer { 0%{background-position:200% 0} 100%{background-position:-200% 0} }

View File

@@ -1,70 +1,192 @@
/* ── Ad Card ── */
/* ── Post Card (Instagram-style vertical post) ── */
.f-card {
flex-shrink: 0;
background: var(--bg-card);
border: 1px solid var(--border); border-radius: 20px; overflow: hidden;
transition: border-color .2s, transform .2s, background .2s; cursor: pointer;
border: 1px solid var(--border); border-radius: 16px; overflow: hidden;
transition: border-color .2s;
cursor: pointer;
}
.f-card:hover { border-color: rgba(var(--teal-rgb), .3); transform: translateY(-2px); }
.f-card:hover { border-color: var(--muted2); }
.f-card-inner {
display: flex; flex-direction: row; min-height: 180px;
/* ── Post header ── */
.f-card-head {
display: flex; align-items: center; gap: 12px;
padding: 12px 14px;
}
/* Image Panel */
.f-card-avatar-ring {
width: 42px; height: 42px; border-radius: 50%;
background: linear-gradient(45deg, #feda75, #fa7e1e, #d62976, #962fbf, #4f5bd5);
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.f-card-avatar {
width: 36px; height: 36px; border-radius: 50%;
background: var(--bg-card);
display: flex; align-items: center; justify-content: center;
font-size: 1.05rem; overflow: hidden;
}
.f-card-avatar img { width: 100%; height: 100%; object-fit: cover; }
.f-card-author { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.f-card-author-name {
color: var(--text-primary); font-weight: 800; font-size: 1.1rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.f-card-author-name.f-truncatable { cursor: pointer; }
.f-card-author-name.f-expanded {
white-space: normal;
overflow: visible;
text-overflow: clip;
}
.f-card-author-loc {
color: var(--muted); font-size: .78rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.f-card-head-side { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* ── Image panel ── */
.f-card-img {
width: 180px; min-width: 180px;
align-self: stretch;
position: relative;
width: 100%;
aspect-ratio: 1 / 1;
background: var(--bg-card-img);
display: flex; align-items: center; justify-content: center;
position: relative; overflow: hidden; flex-shrink: 0;
overflow: hidden;
}
.f-card-img img {
width: 100%; height: 100%;
object-fit: cover; object-position: center;
display: block;
}
.f-card-emoji { font-size: 4rem; opacity: .85; filter: drop-shadow(0 4px 12px rgba(0,0,0,.3)); }
.f-card-reward {
position: absolute; top: 12px; right: 12px; z-index: 2;
background: rgba(0,0,0,.55); backdrop-filter: blur(6px);
border-radius: 999px; padding: 5px 12px;
font-size: .75rem; font-weight: 800;
color: #f5d76e;
}
.f-card-img img { width: 100%; height: 100%; object-fit: contain; object-position: center; display: block; position: absolute; inset: 0; }
.f-card-emoji { font-size: 3rem; opacity: .22; }
/* Badges */
.f-card-badges {
display: flex; align-items: center;
gap: 6px; margin-bottom: 8px;
position: relative;
/* ── Action row ── */
.f-card-actions {
display: flex; align-items: center; justify-content: space-between;
padding: 10px 12px 4px;
}
.f-cat-badge {
background: var(--bg-surface); backdrop-filter: blur(8px);
border: 1px solid var(--border); border-radius: 20px;
padding: 3px 10px; font-size: .72rem; font-weight: 700; color: var(--teal);
.f-card-actions-left, .f-card-actions-right {
display: flex; align-items: center; gap: 6px;
}
.f-type-badge {
border-radius: 20px; padding: 3px 10px;
font-size: .72rem; font-weight: 900;
.f-card-icon-btn {
background: none; border: none; cursor: pointer;
color: var(--text-primary);
width: 38px; height: 38px;
display: flex; align-items: center; justify-content: center;
border-radius: 50%;
transition: background .15s, transform .15s, color .15s;
padding: 0;
}
.f-card-icon-btn:hover { background: var(--hover-bg); transform: scale(1.08); }
.f-card-icon-btn:active { transform: scale(.92); }
.f-card-icon-btn.liked { color: #ed4956; }
.f-card-icon-btn.liked svg { fill: #ed4956; stroke: #ed4956; }
.f-card-icon-btn svg { transition: transform .15s; width: 24px; height: 24px; }
.f-card-like-count {
color: var(--text-primary); font-weight: 800; font-size: .9rem;
line-height: 1;
}
.f-card-reward-text {
display: inline-block;
font-size: .74rem; font-weight: 600; letter-spacing: .02em;
white-space: nowrap;
background: linear-gradient(110deg, #e6b800 30%, #ffec99 50%, #e6b800 70%);
background-size: 200% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: f-reward-shimmer 5s ease-in-out infinite;
}
@keyframes f-reward-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* ── Likes ── */
.f-card-likes {
padding: 2px 16px;
color: var(--text-primary); font-weight: 800; font-size: .9rem;
}
/* ── Caption ── */
.f-card-caption {
padding: 6px 16px;
color: var(--text-primary); font-size: .87rem; line-height: 1.5;
}
.f-card-caption-name { font-weight: 800; margin-right: 6px; }
.f-card-caption-text {
color: var(--text-primary);
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.f-card-caption-text.f-truncatable { cursor: pointer; }
.f-card-caption-text.f-expanded {
-webkit-line-clamp: unset;
line-clamp: unset;
overflow: visible;
}
/* ── Meta pills ── */
.f-card-meta {
display: flex; flex-wrap: wrap; align-items: center;
gap: 6px; padding: 8px 16px 4px;
}
.f-card-meta .f-date-pill {
margin-left: auto;
}
.f-type-loss { background: rgba(255,107,107,.2); color: #ff6b6b; border: 1px solid rgba(255,107,107,.3); }
.f-type-found { background: rgba(var(--teal-rgb), .2); color: var(--teal); border: 1px solid rgba(var(--teal-rgb), .3); }
.f-meta-pill {
display: inline-flex; align-items: center; gap: 4px;
background: var(--hover-bg);
border-radius: 999px; padding: 3px 10px;
font-size: .74rem; color: var(--muted);
border: 1px solid transparent;
}
.f-cat-badge {
display: inline-flex; align-items: center;
background: rgba(var(--teal-rgb), .08);
border: 1px solid rgba(var(--teal-rgb), .25);
border-radius: 999px; padding: 3px 10px;
font-size: .74rem; font-weight: 700; color: var(--teal);
}
.f-type-badge {
border-radius: 999px; padding: 3px 10px;
font-size: .74rem; font-weight: 800;
}
.f-type-loss { background: rgba(237,73,86,.1); color: #ed4956; border: 1px solid rgba(237,73,86,.25); }
.f-type-found { background: rgba(var(--teal-rgb), .1); color: var(--teal); border: 1px solid rgba(var(--teal-rgb), .25); }
/* Status badge for closed/found posts */
.f-status-badge {
border-radius: 20px; padding: 2px 8px;
font-size: .7rem; font-weight: 900;
border-radius: 999px; padding: 2px 8px;
font-size: .7rem; font-weight: 800;
display: inline-block; vertical-align: middle;
margin-left: 8px;
}
.f-status-closed {
background: rgba(72,187,120,.2); color: #48bb78; border: 1px solid rgba(72,187,120,.3);
background: rgba(48,168,94,.12); color: #30a85e; border: 1px solid rgba(48,168,94,.3);
}
/* ── Role badges (LOST_AND_FOUND / METRO) ── */
.f-role-badge {
display: inline-flex; align-items: center; gap: 4px;
border-radius: 20px; padding: 3px 8px;
border-radius: 999px; padding: 3px 8px;
font-size: .68rem; font-weight: 800;
white-space: nowrap;
}
.f-role-lost-found {
background: rgba(255,200,50,.18); color: #f5c842;
border: 1px solid rgba(255,200,50,.3);
background: rgba(255,200,50,.15); color: #b8890f;
border: 1px solid rgba(255,200,50,.35);
}
.f-role-metro {
background: rgba(227,6,19,.15); color: #e30613;
background: rgba(227,6,19,.1); color: #e30613;
border: 1px solid rgba(227,6,19,.25);
}
.f-role-metro-letter {
@@ -74,121 +196,49 @@
font-size: 9px; font-weight: 900; line-height: 1;
}
/* Role-based card border */
.f-card-role-lost-found {
border-color: rgba(255,200,50,.3);
box-shadow: 0 0 20px rgba(255,200,50,.08), inset 0 0 20px rgba(255,200,50,.04);
}
.f-card-role-lost-found:hover {
border-color: rgba(255,200,50,.5);
box-shadow: 0 0 30px rgba(255,200,50,.15), inset 0 0 25px rgba(255,200,50,.06);
}
.f-card-role-metro {
border-color: rgba(227,6,19,.25);
box-shadow: 0 0 20px rgba(227,6,19,.08), inset 0 0 20px rgba(227,6,19,.04);
}
.f-card-role-metro:hover {
border-color: rgba(227,6,19,.45);
box-shadow: 0 0 30px rgba(227,6,19,.15), inset 0 0 25px rgba(227,6,19,.06);
}
/* Body */
.f-card-body {
flex: 1; padding: 14px 16px;
display: flex; flex-direction: column; min-width: 0;
}
.f-card-title { color: var(--text-primary); font-weight: 900; font-size: 1rem; margin-bottom: 5px; line-height: 1.3; }
.f-card-desc {
color: var(--muted); font-size: .82rem; line-height: 1.5; margin-bottom: 10px;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Meta pills */
.f-card-meta {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
}
.f-meta-pill {
display: flex;
align-items: center;
gap: 4px;
background: var(--hover-bg);
border: 1px solid var(--border);
border-radius: 20px;
padding: 3px 9px;
font-size: .73rem;
color: var(--muted);
}
.f-date-pill {
cursor: pointer;
position: relative;
margin-left: auto;
}
.f-date-pill:hover {
background: rgba(128,128,128,.12);
}
/* Map stub */
.f-card-map {
width: 100%; height: 76px;
background: var(--bg-input); margin-bottom: 14px; border-radius: 12px;
border: 1px solid var(--border);
display: flex; align-items: center; justify-content: center;
gap: 8px; color: var(--muted); font-size: .78rem;
cursor: pointer; transition: border-color .2s;
font-family: var(--font); font-weight: 600;
}
.f-card-map:hover { border-color: rgba(var(--teal-rgb), .3); color: var(--teal); }
/* Footer */
/* ── Footer (phone + owner actions) ── */
.f-card-foot {
display: flex; align-items: center; justify-content: space-between;
padding-top: 10px; border-top: 1px solid var(--border); margin-top: auto;
gap: 8px; padding: 10px 16px 12px;
flex-wrap: wrap;
}
.f-card-foot-left { display: flex; align-items: center; }
.f-card-foot-right { display: flex; align-items: center; gap: 6px; }
.f-card-foot-left { display: flex; align-items: center; min-width: 0; }
.f-card-foot-right { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.f-card-phone {
color: var(--teal); font-size: .82rem; font-weight: 700;
text-decoration: none; display: flex; align-items: center; gap: 5px;
color: var(--teal); font-size: .85rem; font-weight: 700;
text-decoration: none; display: flex; align-items: center; gap: 6px;
}
.f-card-date { color: var(--muted2); font-size: .75rem; }
.f-like-btn {
.f-card-contact {
display: flex; align-items: center; gap: 5px;
background: none; border: 1px solid var(--border);
border-radius: 20px; padding: 5px 11px; cursor: pointer;
font-family: var(--font); font-size: .8rem; font-weight: 700;
color: var(--muted); transition: all .2s; line-height: 1;
background: rgba(var(--teal-rgb), .1);
border: 1px solid rgba(var(--teal-rgb), .3);
border-radius: 999px; padding: 12px 32px;
cursor: pointer; font-family: var(--font);
font-size: 1.05rem; font-weight: 700;
color: var(--teal); transition: all .2s;
}
.f-like-btn:hover, .f-like-btn.liked {
border-color: rgba(255,107,107,.5); color: #ff6b6b; background: rgba(255,107,107,.08);
.f-card-contact:hover {
background: rgba(var(--teal-rgb), .18);
border-color: rgba(var(--teal-rgb), .5);
}
/* Closed/Found card styling */
.f-card-closed {
opacity: 0.7;
border-color: rgba(72,187,120,.3);
}
.f-card-closed:hover {
border-color: rgba(72,187,120,.5);
transform: none;
}
.f-card-closed .f-card-img {
filter: grayscale(0.5);
}
.f-card-closed .f-card-title {
text-decoration: line-through;
opacity: 0.7;
opacity: .8;
border-color: rgba(48,168,94,.3);
}
.f-card-closed:hover { border-color: rgba(48,168,94,.5); }
.f-card-closed .f-card-img { filter: grayscale(.5); }
.f-card-closed .f-card-caption-name { text-decoration: line-through; opacity: .7; }
/* Edit & Delete buttons */
.f-card-edit-btn,
.f-card-delete-btn {
display: flex; align-items: center; justify-content: center;
background: none; border: 1px solid var(--border);
border-radius: 8px; width: 32px; height: 32px;
border-radius: 50%; width: 34px; height: 34px;
cursor: pointer; transition: all .2s;
color: var(--muted); padding: 0;
}
@@ -196,51 +246,20 @@
border-color: rgba(var(--teal-rgb), .5); color: var(--teal); background: rgba(var(--teal-rgb), .08);
}
.f-card-delete-btn:hover {
border-color: rgba(255,107,107,.5); color: #ff6b6b; background: rgba(255,107,107,.08);
border-color: rgba(237,73,86,.5); color: #ed4956; background: rgba(237,73,86,.08);
}
/* Mark as Found button */
.f-card-found-btn {
display: flex; align-items: center; gap: 5px;
background: rgba(72,187,120,.1); border: 1px solid rgba(72,187,120,.3);
border-radius: 20px; padding: 5px 11px;
background: rgba(48,168,94,.1); border: 1px solid rgba(48,168,94,.3);
border-radius: 999px; padding: 5px 11px;
cursor: pointer; transition: all .2s;
font-family: var(--font); font-size: .75rem; font-weight: 700;
color: #48bb78;
color: #30a85e;
}
.f-card-found-btn:hover {
background: rgba(72,187,120,.2); border-color: rgba(72,187,120,.5);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(72,187,120,.2);
}
@media (max-width: 600px) {
/* Карточка становится вертикальной */
.f-card-inner {
flex-direction: column;
min-height: unset;
}
/* Картинка — горизонтальная полоса сверху */
.f-card-img {
width: 100%;
min-width: unset;
height: 180px;
}
/* Убираем кнопку карты на мобилке — занимает много места */
.f-card-map {
display: none;
}
/* Чуть меньше паддинг в теле карточки */
.f-card-body {
padding: 12px;
}
.f-card-phone {
font-size: 0.72rem;
}
background: rgba(48,168,94,.18); border-color: rgba(48,168,94,.5);
}
/* ── Calendar Popup ── */
@@ -250,7 +269,7 @@
border-radius: 12px;
padding: 10px;
width: 220px;
box-shadow: 0 8px 32px rgba(0,0,0,.25);
box-shadow: 0 8px 32px rgba(0,0,0,.2);
}
.f-date-calendar-overlay {
position: fixed;
@@ -265,6 +284,7 @@
margin-bottom: 8px;
font-size: .82rem;
font-weight: 700;
color: var(--text-primary);
}
.f-cal-header button {
background: none;
@@ -277,7 +297,7 @@
line-height: 1;
}
.f-cal-header button:hover {
background: rgba(128,128,128,.15);
background: var(--hover-bg);
}
.f-cal-grid {
display: grid;
@@ -311,11 +331,7 @@
color: #d4af37;
user-select: none;
}
.f-reward-checkbox input {
display: none;
}
.f-reward-checkbox input { display: none; }
.f-reward-checkmark {
width: 20px;
height: 20px;
@@ -325,12 +341,10 @@
transition: .2s;
position: relative;
}
.f-reward-checkbox input:checked + .f-reward-checkmark {
background: #d4af37;
box-shadow: 0 0 10px rgba(212, 175, 55, .35);
}
.f-reward-checkbox input:checked + .f-reward-checkmark::after {
content: "";
position: absolute;
@@ -342,23 +356,13 @@
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.f-reward-text {
font-size: .9rem;
font-weight: 700;
}
.f-card-img {
.f-date-pill {
cursor: pointer;
position: relative;
}
.f-card-reward {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
font-size: .72rem;
font-weight: 800;
color: #f5d76e;
}
.f-date-pill:hover { background: var(--hover-bg); }

View File

@@ -1,10 +1,10 @@
/* ── Chat Page ── */
/* ── Chat Page (Instagram Direct style) ── */
.chat-page {
display: flex;
flex-direction: column;
flex: 1;
height: 100vh;
background: linear-gradient(135deg, #1a2340 0%, #0d1018 100%);
background: var(--bg-app);
}
/* Header */
@@ -12,9 +12,9 @@
display: flex;
align-items: center;
gap: 14px;
padding: 14px 18px;
padding: 12px 18px;
border-bottom: 1px solid var(--border);
background: rgba(13, 16, 24, .7);
background: var(--bg-surface-solid);
backdrop-filter: blur(12px);
}
.chat-back-btn {
@@ -28,30 +28,30 @@
transition: background .15s, color .15s;
}
.chat-back-btn:hover {
background: rgba(255, 255, 255, .08);
color: #fff;
background: var(--hover-bg);
color: var(--text-primary);
}
.chat-header-title {
flex: 1;
color: #fff;
font-weight: 900;
font-size: 1.1rem;
color: var(--text-primary);
font-weight: 800;
font-size: 1rem;
}
.chat-status {
font-size: .75rem;
font-size: .72rem;
font-weight: 700;
color: var(--muted);
}
.chat-status.connected {
color: #2de0c8;
color: #30a85e;
}
.chat-header-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(45, 224, 200, .15);
border: 2px solid rgba(45, 224, 200, .3);
background: var(--bg-input);
border: 2px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
@@ -73,10 +73,10 @@
padding: 20px;
display: flex;
flex-direction: column;
gap: 10px;
gap: 8px;
}
.chat-messages::-webkit-scrollbar { width: 4px; }
.chat-messages::-webkit-scrollbar-thumb { background: rgba(255,255,255,.1); border-radius: 2px; }
.chat-messages::-webkit-scrollbar-thumb { background: var(--muted2); border-radius: 2px; }
.chat-empty {
text-align: center;
@@ -97,18 +97,20 @@
.chat-bubble {
max-width: 70%;
padding: 10px 14px;
border-radius: 14px;
font-size: .93rem;
border-radius: 18px;
font-size: .92rem;
line-height: 1.45;
align-self: flex-start;
background: rgba(255, 255, 255, .08);
color: #fff;
background: var(--bg-surface-solid);
border: 1px solid var(--border);
color: var(--text-primary);
word-break: break-word;
}
.chat-bubble.mine {
align-self: flex-end;
background: #2de0c8;
color: #082320;
background: #3797f0;
border-color: #3797f0;
color: #fff;
}
.chat-bubble-time {
display: block;
@@ -123,39 +125,38 @@
/* Input row */
.chat-input-row {
display: flex;
gap: 10px;
padding: 16px;
gap: 8px;
padding: 14px 16px;
border-top: 1px solid var(--border);
background: rgba(13, 16, 24, .5);
backdrop-filter: blur(8px);
background: var(--bg-surface-solid);
}
.chat-input {
flex: 1;
padding: 12px 16px;
border-radius: 12px;
padding: 12px 18px;
border-radius: 999px;
border: 1px solid var(--border);
background: rgba(255, 255, 255, .08);
color: #fff;
background: var(--bg-input);
color: var(--text-primary);
font-family: var(--font);
font-size: .9rem;
outline: none;
transition: border-color .2s;
transition: border-color .2s, box-shadow .2s;
}
.chat-input::placeholder { color: var(--muted); }
.chat-input:focus { border-color: rgba(45, 224, 200, .5); }
.chat-input:focus { border-color: rgba(var(--teal-rgb), .5); box-shadow: 0 0 0 3px rgba(var(--teal-rgb), .12); }
.chat-send-btn {
padding: 0 20px;
border-radius: 12px;
border-radius: 999px;
border: none;
background: #2de0c8;
color: #082320;
background: var(--teal);
color: #fff;
cursor: pointer;
font-size: 1.1rem;
font-size: 1rem;
font-weight: 900;
transition: background .2s, opacity .2s;
}
.chat-send-btn:hover:not(:disabled) { background: #24c9b2; }
.chat-send-btn:hover:not(:disabled) { background: var(--teal2); }
.chat-send-btn:disabled { opacity: .4; cursor: default; }
/* ── Emoji picker ── */
@@ -172,23 +173,21 @@
.chat-emoji-picker em-emoji-picker {
width: 320px;
max-height: 350px;
--rgb-background: 13, 16, 24;
--rgb-accent: 45, 224, 200;
}
.chat-emoji-btn {
background: rgba(255, 255, 255, .08);
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 12px;
border-radius: 999px;
padding: 0 12px;
cursor: pointer;
font-size: 1.3rem;
font-size: 1.2rem;
line-height: 1;
transition: background .15s, border-color .15s;
}
.chat-emoji-btn:hover,
.chat-emoji-btn.active {
background: rgba(255, 255, 255, .14);
border-color: rgba(45, 224, 200, .5);
background: var(--hover-bg);
border-color: rgba(var(--teal-rgb), .5);
}
/* ── Chat List ── */
@@ -196,37 +195,38 @@
flex: 1;
overflow-y: auto;
padding: 8px;
max-width: 560px;
width: 100%;
margin: 0 auto;
}
.chat-list::-webkit-scrollbar { width: 4px; }
.chat-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,.1); border-radius: 2px; }
.chat-list::-webkit-scrollbar-thumb { background: var(--muted2); border-radius: 2px; }
.chat-list-item {
display: flex;
align-items: center;
gap: 14px;
padding: 14px 16px;
padding: 12px 16px;
border-radius: 14px;
cursor: pointer;
transition: background .15s;
border: 1px solid transparent;
}
.chat-list-item:hover {
background: rgba(255, 255, 255, .05);
border-color: rgba(45, 224, 200, .2);
background: var(--hover-bg);
}
.chat-list-avatar {
width: 44px;
height: 44px;
border-radius: 50%;
background: rgba(45, 224, 200, .15);
border: 2px solid rgba(45, 224, 200, .3);
background: var(--bg-input);
border: 2px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.15rem;
font-weight: 800;
color: #2de0c8;
color: var(--muted);
flex-shrink: 0;
overflow: hidden;
}
@@ -242,14 +242,14 @@
min-width: 0;
}
.chat-list-item.unread .chat-list-name {
color: #2de0c8;
font-weight: 800;
}
.chat-list-item.unread .chat-list-preview {
color: rgba(45, 224, 200, .6);
font-weight: 600;
color: var(--text-primary);
font-weight: 700;
}
.chat-list-name {
color: #fff;
color: var(--text-primary);
font-weight: 700;
font-size: .9rem;
white-space: nowrap;
@@ -267,10 +267,10 @@
/* ── Unread Badge ── */
.chat-list-unread-badge {
background: #2de0c8;
color: #082320;
background: #ff3040;
color: #fff;
font-size: .7rem;
font-weight: 900;
font-weight: 800;
min-width: 20px;
height: 20px;
border-radius: 10px;
@@ -283,10 +283,10 @@
/* ── Sidebar Unread Badge ── */
.f-sb-unread-badge {
background: #2de0c8;
color: #082320;
background: #ff3040;
color: #fff;
font-size: .65rem;
font-weight: 900;
font-weight: 800;
min-width: 18px;
height: 18px;
border-radius: 9px;
@@ -302,24 +302,24 @@
display: flex;
align-items: center;
gap: 5px;
background: rgba(45, 224, 200, .12);
border: 1px solid rgba(45, 224, 200, .3);
border-radius: 20px;
background: rgba(var(--teal-rgb), .1);
border: 1px solid rgba(var(--teal-rgb), .3);
border-radius: 999px;
padding: 5px 14px;
cursor: pointer;
font-family: var(--font);
font-size: .8rem;
font-weight: 700;
color: #2de0c8;
color: var(--teal);
transition: all .2s;
}
.f-card-contact:hover {
background: rgba(45, 224, 200, .22);
border-color: rgba(45, 224, 200, .5);
background: rgba(var(--teal-rgb), .18);
border-color: rgba(var(--teal-rgb), .5);
}
.chat-list-delete-btn {
background: rgba(255, 71, 87, .1);
background: rgba(255, 71, 87, .08);
border: 1px solid rgba(255, 71, 87, .2);
border-radius: 10px;
color: rgba(255, 71, 87, .5);
@@ -336,7 +336,7 @@
opacity: 1;
}
.chat-list-delete-btn:hover {
background: rgba(255, 71, 87, .2);
background: rgba(255, 71, 87, .15);
border-color: rgba(255, 71, 87, .4);
color: #ff4757;
}
@@ -344,10 +344,10 @@
/* Mobile adjustments */
@media (max-width: 600px) {
.chat-header {
padding: 12px 14px;
padding: 10px 12px;
}
.chat-input-row {
padding: 12px;
padding: 10px 12px;
}
.chat-bubble {
max-width: 85%;

View File

@@ -1,4 +1,4 @@
/* ── App Shell ── */
/* ── App Shell (Instagram-style) ── */
.f-app {
display: flex; height: 100vh;
background: var(--bg-app);
@@ -18,12 +18,12 @@
min-width: 0; height: 100vh; overflow: hidden;
}
/* ── Topbar ── */
/* ── Topbar (mobile only; desktop uses the nav sidebar) ── */
.f-topbar {
display: flex; align-items: center; gap: 12px;
padding: 14px 16px;
display: flex; align-items: center; gap: 8px;
padding: 8px 16px;
border-bottom: 1px solid var(--border);
background: var(--bg-surface); backdrop-filter: blur(12px);
background: var(--bg-surface);
position: sticky; top: 0; z-index: 10;
}
.f-menu-btn {
@@ -33,7 +33,11 @@
color: var(--text-primary);
}
.f-menu-btn:hover { background: var(--hover-bg); }
.f-topbar-center { flex: 1; display: flex; justify-content: center; }
.f-topbar-center { flex: 1; display: flex; justify-content: center; min-width: 0; }
.f-topbar-wordmark {
color: var(--text-primary); font-weight: 900; font-size: 1.05rem;
letter-spacing: -.4px; white-space: nowrap; margin-left: 2px;
}
.f-topbar-spacer { width: 32px; flex-shrink: 0; }
/* ── Topbar Profile ── */
@@ -55,8 +59,8 @@
/* ── Search Bar ── */
.f-search-bar {
display: flex; align-items: center; gap: 10px;
padding: 8px 16px;
background: var(--bg-filter); backdrop-filter: blur(8px);
padding: 10px 16px;
background: var(--bg-surface);
border-bottom: 1px solid var(--border);
}
@@ -68,58 +72,53 @@
.f-search-input {
flex: 1; min-width: 0;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 14px;
border: 1px solid transparent;
border-radius: 999px;
padding: 10px 18px;
font-family: var(--font); font-size: 0.9rem;
color: var(--text-primary);
outline: none;
transition: border-color .2s, box-shadow .2s;
transition: border-color .2s, box-shadow .2s, background .2s;
}
.f-search-input::placeholder { color: var(--muted); }
.f-search-input:focus {
border-color: rgba(var(--teal-rgb), .5);
box-shadow: 0 0 0 3px rgba(var(--teal-rgb), .12);
border-color: var(--border);
background: var(--bg-surface);
box-shadow: 0 1px 6px rgba(0,0,0,.06);
}
.f-search-clear {
background: none; border: none; cursor: pointer;
color: var(--muted); padding: 4px; border-radius: 6px;
color: var(--muted); padding: 4px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0; transition: color .15s, background .15s;
}
.f-search-clear:hover { color: var(--text-primary); background: var(--hover-bg); }
@media (max-width: 768px) {
.f-search-bar { padding: 8px 12px; }
.f-search-input { padding: 8px 12px; font-size: 0.85rem; }
}
/* ── Filter Bar ── */
.f-filter-bar {
display: flex; gap: 10px;
padding: 12px 16px;
display: flex; gap: 8px;
padding: 10px 16px;
border-bottom: 1px solid var(--border);
background: var(--bg-filter); backdrop-filter: blur(8px);
background: var(--bg-surface);
justify-content: center;
}
.f-filter-btn {
display: flex; align-items: center; gap: 6px;
padding: 10px 18px; border-radius: 12px;
font-family: var(--font); font-size: 0.9rem; font-weight: 600;
background: var(--hover-bg); border: 1px solid var(--border);
padding: 8px 16px; border-radius: 999px;
font-family: var(--font); font-size: 0.85rem; font-weight: 600;
background: var(--bg-input); border: 1px solid var(--border);
color: var(--muted); cursor: pointer;
transition: all 0.25s ease;
transition: all 0.2s ease;
}
.f-filter-btn:hover {
background: var(--hover-bg); border-color: rgba(var(--teal-rgb), .3);
color: var(--text-primary);
background: var(--hover-bg); color: var(--text-primary);
}
.f-filter-btn.active {
background: rgba(var(--teal-rgb), .15); border-color: rgba(var(--teal-rgb), .5);
color: var(--teal); box-shadow: 0 0 12px rgba(var(--teal-rgb), .25);
font-weight: 700;
background: rgba(var(--teal-rgb), .08);
border-color: rgba(var(--teal-rgb), .5);
color: var(--teal); font-weight: 700;
}
/* ── City Multiselect ── */
@@ -128,7 +127,7 @@
display: flex; align-items: center;
padding: 10px 16px;
border-bottom: 1px solid var(--border);
background: var(--bg-filter); backdrop-filter: blur(8px);
background: var(--bg-surface);
}
.f-city-multiselect {
@@ -140,9 +139,9 @@
display: flex; align-items: center; gap: 8px;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 12px;
padding: 8px 12px;
min-height: 44px;
border-radius: 999px;
padding: 6px 14px;
min-height: 42px;
cursor: text;
transition: border-color .2s, box-shadow .2s;
}
@@ -162,12 +161,12 @@
.f-city-ms-tag {
display: inline-flex; align-items: center; gap: 4px;
background: rgba(var(--teal-rgb), .15);
border: 1px solid rgba(var(--teal-rgb), .4);
background: rgba(var(--teal-rgb), .1);
border: 1px solid rgba(var(--teal-rgb), .35);
color: var(--teal);
border-radius: 999px;
padding: 4px 10px;
font-size: 0.8rem; font-weight: 600;
padding: 3px 10px;
font-size: 0.78rem; font-weight: 600;
white-space: nowrap;
}
.f-city-ms-tag-remove {
@@ -202,10 +201,10 @@
.f-city-ms-dropdown {
position: absolute; top: calc(100% + 6px); left: 0; right: 0;
background: var(--bg-surface);
background: var(--bg-surface-solid);
border: 1px solid var(--border);
border-radius: 12px;
box-shadow: 0 12px 32px rgba(0, 0, 0, .4);
box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
max-height: 260px; overflow-y: auto;
z-index: 30; padding: 6px;
}
@@ -233,7 +232,7 @@
}
.f-city-ms-option.selected .f-city-ms-check {
border-color: var(--teal);
background: rgba(var(--teal-rgb), .15);
background: rgba(var(--teal-rgb), .12);
}
.f-city-ms-option.f-city-ms-add .f-city-ms-check {
border-color: rgba(var(--teal-rgb), .5);
@@ -243,61 +242,134 @@
padding: 10px; text-align: center; color: var(--muted); font-size: 0.85rem;
}
/* ── Bottom Navigation (Instagram-style) ── */
.f-bottom-nav {
position: fixed; left: 0; right: 0; bottom: 0;
display: flex; align-items: center; justify-content: space-around;
padding: 6px 0 calc(6px + env(safe-area-inset-bottom));
background: var(--bg-surface-solid);
border-top: 1px solid var(--border);
z-index: 35;
}
.f-bn-item {
position: relative;
display: flex; align-items: center; justify-content: center;
width: 44px; height: 44px;
border: none; background: none; cursor: pointer;
color: var(--text-primary);
transition: color .15s, transform .1s;
}
.f-bn-item:active { transform: scale(.92); }
.f-bn-item svg { width: 26px; height: 26px; }
.f-bn-item.active { color: var(--teal); }
.f-bn-item.active::before {
content: ""; position: absolute; inset: 3px;
border-radius: 50%;
background: rgba(var(--teal-rgb), .12);
}
/* Create button — бирюзовая рамка как у старой кнопки «Разместить» */
.f-bn-create {
width: 44px; height: 44px;
border-radius: 999px;
background: var(--teal);
color: #fff;
box-shadow: 0 2px 10px rgba(var(--teal-rgb), .35);
}
.f-bn-create:hover { background: var(--teal2); }
.f-bn-create svg { color: #fff; }
/* Profile avatar inside the nav */
.f-bn-profile .f-profile-avatar,
.f-bn-profile .f-profile-avatar-placeholder {
width: 28px; height: 28px;
}
.f-bn-profile .f-profile-avatar-placeholder { font-size: 1rem; }
.f-bn-item.f-bn-profile.active::before {
inset: -1px;
box-shadow: 0 0 0 2px var(--teal);
}
/* Indicator: активные фильтры поиска */
.f-bn-dot {
position: absolute; top: 6px; right: 4px;
width: 8px; height: 8px; border-radius: 50%;
background: #ff3040;
box-shadow: 0 0 0 2px var(--bg-surface-solid);
}
/* Search panel — видна только во вкладке «Поиск» (на десктопе всегда) */
.f-search-panel { display: none; }
.f-app.f-search-view .f-search-panel { display: block; }
/* ── Feed Wrap + Scroll ── */
.f-wrap { display: flex; flex: 1; overflow: hidden; min-height: 0; }
.f-feed {
flex: 1; overflow-y: scroll; min-height: 0;
padding: 16px; display: flex; flex-direction: column; gap: 14px;
padding: 20px 16px; display: flex; flex-direction: column; gap: 16px;
width: 100%; max-width: 660px; margin: 0 auto;
}
.f-feed::-webkit-scrollbar { width: 4px; }
.f-feed::-webkit-scrollbar-track { background: transparent; }
.f-feed::-webkit-scrollbar-thumb { background: var(--muted2); border-radius: 2px; }
@media (max-width: 768px) {
/* Лента занимает весь экран без отступов */
@media (min-width: 900px) {
.f-main-feed .f-topbar {
display: none;
}
.f-search-panel {
display: block;
}
.f-bottom-nav {
display: none;
}
}
@media (max-width: 900px) {
.f-feed {
padding: 10px;
gap: 10px;
gap: 12px;
}
/* Топбар компактнее */
.f-topbar {
padding: 10px 12px;
padding: 8px 12px;
}
/* Кнопка создания объявления — как «Войти» */
.f-post-btn {
padding: 10px 14px;
font-size: 0.8rem;
padding: 8px 14px;
font-size: 0.78rem;
}
.f-login-btn {
padding: 6px 14px;
font-size: 0.8rem;
font-size: 0.78rem;
}
/* Фильтр компактнее */
.f-filter-bar {
padding: 10px 12px;
gap: 8px;
padding: 8px 12px;
gap: 6px;
}
.f-filter-btn {
padding: 8px 14px;
font-size: 0.85rem;
padding: 7px 13px;
font-size: 0.82rem;
}
/* Городской фильтр компактнее */
.f-city-filter-wrap { padding: 8px 12px; }
.f-city-ms-control { padding: 6px 10px; min-height: 40px; }
.f-city-ms-control { padding: 5px 12px; min-height: 38px; }
.f-city-ms-input { font-size: 0.85rem; }
.f-city-ms-tag { font-size: 0.75rem; padding: 3px 8px; }
.f-city-ms-tag { font-size: 0.75rem; padding: 2px 8px; }
/* Нижняя панель fixed — освобождаем место в конце ленты */
.f-feed {
padding-bottom: calc(72px + env(safe-area-inset-bottom));
}
}
@media (max-width: 600px) {
.f-post-btn {
padding: 8px 8px;
font-size: 0.68rem;
padding: 7px 10px;
font-size: 0.7rem;
gap: 4px;
border-radius: 8px;
}
@@ -309,13 +381,13 @@
}
.f-login-btn {
padding: 4px 8px;
font-size: 0.68rem;
padding: 4px 10px;
font-size: 0.7rem;
border-radius: 8px;
}
.f-filter-btn {
padding: 8px 12px;
font-size: 0.8rem;
padding: 7px 12px;
font-size: 0.78rem;
}
}

View File

@@ -43,7 +43,7 @@
.f-m-field:focus { border-color: var(--teal); background: rgba(var(--teal-rgb), .07); box-shadow: 0 0 0 3px rgba(var(--teal-rgb), .12); }
.f-m-field::placeholder { color: var(--muted); }
.f-m-field option { background: var(--bg-surface-solid); color: var(--text-primary); }
.f-m-field.err { border-color: rgba(255,107,107,.55); }
.f-m-field.err { border-color: rgba(237,73,86,.55); }
/* Type Toggle */
.f-type-toggle { display: flex; gap: 8px; }
@@ -53,7 +53,7 @@
color: var(--muted); font-family: var(--font); font-size: .85rem;
font-weight: 800; cursor: pointer; text-align: center; transition: all .2s;
}
.f-type-opt.loss { border-color: rgba(255,107,107,.5); background: rgba(255,107,107,.12); color: #ff6b6b; }
.f-type-opt.loss { border-color: rgba(237,73,86,.5); background: rgba(237,73,86,.12); color: #ff6b6b; }
.f-type-opt.found { border-color: rgba(var(--teal-rgb), .5); background: rgba(var(--teal-rgb), .1); color: var(--teal); }
/* Map picker */
@@ -211,13 +211,13 @@
/* Submit Button */
.f-m-submit {
width: 100%;
background: linear-gradient(90deg, var(--teal2), var(--teal), var(--teal2));
background-size: 200% 100%; border: none; border-radius: 13px;
padding: 14px; color: #082320; font-family: var(--font);
font-size: 1rem; font-weight: 900; cursor: pointer; margin-top: 6px;
animation: shimmer 3s linear infinite; transition: opacity .15s, transform .15s;
background: var(--teal);
border: none; border-radius: 13px;
padding: 14px; color: #fff; font-family: var(--font);
font-size: 1rem; font-weight: 800; cursor: pointer; margin-top: 6px;
transition: background .15s, opacity .15s, transform .15s;
}
.f-m-submit:hover:not(:disabled) { opacity: .88; transform: translateY(-1px); }
.f-m-submit:hover:not(:disabled) { background: var(--teal2); transform: translateY(-1px); }
.f-m-submit:active:not(:disabled) { transform: translateY(0); }
.f-m-submit:disabled { opacity: .5; cursor: not-allowed; animation: none; background: var(--muted2); color: var(--muted); }
.f-m-submit-row { display: flex; align-items: center; justify-content: center; gap: 8px; }
@@ -225,18 +225,18 @@
/* ── Modal Logout Button ── */
.f-modal-logout {
display: block; width: 100%; margin-top: 16px; padding: 10px;
border-radius: 10px; border: 1px solid rgba(255,107,107,.3);
background: rgba(255,107,107,.08); color: rgba(255,107,107,.8);
border-radius: 10px; border: 1px solid rgba(237,73,86,.3);
background: rgba(237,73,86,.08); color: rgba(237,73,86,.8);
font-family: var(--font); font-size: .9rem; font-weight: 700; cursor: pointer;
transition: background .15s, border-color .15s;
}
.f-modal-logout:hover {
background: rgba(255,107,107,.18);
border-color: rgba(255,107,107,.5);
background: rgba(237,73,86,.18);
border-color: rgba(237,73,86,.5);
}
.f-m-spinner {
width: 16px; height: 16px;
border: 2px solid var(--border); border-top-color: var(--teal);
border: 2px solid rgba(255,255,255,.4); border-top-color: #fff;
border-radius: 50%; animation: spin .7s linear infinite;
}
@@ -302,7 +302,7 @@
.f-rules-input:checked + .f-rules-checkmark::after {
content: ""; position: absolute; left: 5px; top: 1px;
width: 6px; height: 10px;
border: solid #082320; border-width: 0 2.5px 2.5px 0;
border: solid #fff; border-width: 0 2.5px 2.5px 0;
transform: rotate(45deg);
}
.f-rules-input:focus + .f-rules-checkmark {
@@ -357,15 +357,13 @@
.f-result-btn:hover { opacity: .88; transform: translateY(-1px); }
.f-result-btn:active { transform: translateY(0); }
.f-result-btn.ok {
background: linear-gradient(90deg, var(--teal2), var(--teal), var(--teal2));
background-size: 200% 100%;
color: #082320;
animation: shimmer 3s linear infinite;
background: var(--teal);
color: #fff;
}
.f-result-btn.err {
background: rgba(255, 107, 107, .12);
border: 1.5px solid rgba(255, 107, 107, .35);
color: #ff6b6b;
background: rgba(237, 73, 86, .1);
border: 1.5px solid rgba(237, 73, 86, .35);
color: #ed4956;
}
.f-map-frame {

View File

@@ -1,6 +1,6 @@
/* ── Reviews Page ── */
.rv-page-title {
font-size: 1.15rem;
font-size: 1.1rem;
font-weight: 900;
color: var(--text-primary);
margin: 0;
@@ -12,6 +12,9 @@
padding: 20px;
flex: 1;
overflow-y: auto;
max-width: 900px;
width: 100%;
margin: 0 auto;
}
/* ── Left sidebar (summary + form) ── */
@@ -25,7 +28,7 @@
/* ── Summary block ── */
.rv-summary {
background: rgba(255, 255, 255, .04);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 20px;
@@ -77,7 +80,7 @@
.rv-dist-bar {
flex: 1;
height: 6px;
background: rgba(255, 255, 255, .06);
background: var(--bg-input);
border-radius: 3px;
overflow: hidden;
}
@@ -101,7 +104,7 @@
gap: 3px;
}
.rv-star {
color: var(--muted);
color: var(--muted2);
transition: color .15s, transform .15s;
flex-shrink: 0;
}
@@ -117,7 +120,7 @@
/* ── Review Form ── */
.rv-form {
background: rgba(255, 255, 255, .04);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 20px;
@@ -142,14 +145,14 @@
color: var(--muted);
}
.rv-form-error {
color: #ff6b6b;
color: var(--err);
font-size: .8rem;
font-weight: 700;
}
.rv-form-textarea {
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 10px;
border-radius: 12px;
padding: 12px 14px;
color: var(--text-primary);
font-family: var(--font);
@@ -160,7 +163,7 @@
.rv-form-textarea:focus {
outline: none;
border-color: var(--teal);
box-shadow: 0 0 0 3px rgba(45, 224, 200, .12);
box-shadow: 0 0 0 3px rgba(var(--teal-rgb), .12);
}
.rv-form-textarea::placeholder {
color: var(--muted);
@@ -176,19 +179,19 @@
font-weight: 600;
}
.rv-form-submit {
background: linear-gradient(135deg, var(--teal2), var(--teal));
background: var(--teal);
border: none;
border-radius: 10px;
padding: 10px 22px;
color: #111;
color: #fff;
font-family: var(--font);
font-size: .85rem;
font-weight: 800;
cursor: pointer;
transition: opacity .15s, transform .15s;
transition: background .15s, transform .15s;
}
.rv-form-submit:hover:not(:disabled) {
opacity: .9;
background: var(--teal2);
transform: translateY(-1px);
}
.rv-form-submit:disabled {
@@ -198,7 +201,7 @@
/* ── Login prompt ── */
.rv-login-prompt {
background: rgba(255, 255, 255, .04);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 20px;
@@ -210,19 +213,19 @@
margin: 0 0 14px;
}
.rv-login-btn {
background: linear-gradient(135deg, var(--teal2), var(--teal));
background: var(--teal);
border: none;
border-radius: 10px;
padding: 10px 24px;
color: #111;
color: #fff;
font-family: var(--font);
font-size: .85rem;
font-weight: 800;
cursor: pointer;
transition: opacity .15s;
transition: background .15s;
}
.rv-login-btn:hover {
opacity: .9;
background: var(--teal2);
}
/* ── Reviews List ── */
@@ -236,7 +239,7 @@
/* ── Review Card ── */
.rv-card {
background: rgba(255, 255, 255, .04);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 18px 20px;
@@ -247,7 +250,7 @@
animation: rvCardIn .5s cubic-bezier(.34, 1.56, .64, 1) both;
}
.rv-card:hover {
border-color: rgba(45, 224, 200, .25);
border-color: var(--muted2);
}
@keyframes rvCardIn {
from { opacity: 0; transform: translateY(16px); }
@@ -263,14 +266,14 @@
width: 38px;
height: 38px;
border-radius: 50%;
background: rgba(45, 224, 200, .15);
border: 2px solid rgba(45, 224, 200, .3);
background: var(--bg-input);
border: 2px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
font-weight: 800;
color: var(--teal);
color: var(--muted);
flex-shrink: 0;
}
.rv-card-meta {
@@ -288,6 +291,19 @@
overflow: hidden;
text-overflow: ellipsis;
}
.rv-card-pending-badge {
align-self: flex-start;
font-size: .68rem;
font-weight: 700;
color: var(--teal);
background: rgba(var(--teal-rgb), .1);
border-radius: 999px;
padding: 2px 8px;
}
.rv-card-pending {
border-style: dashed;
opacity: .85;
}
.rv-card-date {
font-size: .72rem;
color: var(--muted);

View File

@@ -24,10 +24,10 @@
/* Single section */
.rules-section {
border: 1px solid rgba(255,255,255,.07);
border: 1px solid var(--border);
border-radius: 14px;
overflow: hidden;
background: rgba(255,255,255,.025);
background: var(--bg-card);
}
.rules-section-head {
@@ -35,14 +35,14 @@
align-items: center;
gap: 12px;
padding: 12px 16px 10px;
border-bottom: 1px solid rgba(255,255,255,.06);
border-bottom: 1px solid var(--border);
}
.rules-num {
min-width: 26px;
height: 26px;
background: linear-gradient(135deg, #1fc9b0, #2de0c8);
color: #082320;
background: var(--teal);
color: #fff;
border-radius: 8px;
font-size: .75rem;
font-weight: 900;
@@ -53,7 +53,7 @@
}
.rules-title {
color: #fff;
color: var(--text-primary);
font-weight: 800;
font-size: .92rem;
letter-spacing: -.1px;
@@ -93,7 +93,7 @@
/* Sub-section titles (3.1, 3.2 …) */
.rules-sub-title {
color: rgba(255,255,255,.55);
color: var(--muted);
font-size: .75rem;
font-weight: 800;
text-transform: uppercase;
@@ -113,11 +113,11 @@
/* Warning paragraph */
.rules-warn {
background: rgba(255,107,107,.1);
border: 1px solid rgba(255,107,107,.2);
background: rgba(237,73,86,.08);
border: 1px solid rgba(237,73,86,.25);
border-radius: 8px;
padding: 8px 12px;
color: #ff9999;
color: var(--err);
font-size: .8rem;
margin-top: 8px !important;
}

View File

@@ -1,91 +1,217 @@
/* ── Left Navigation (Instagram-style) ── */
.f-sb-head {
padding: 22px 20px 16px;
padding: 16px 20px 20px;
display: flex; align-items: center; gap: 11px;
border-bottom: 1px solid var(--border);
}
.f-sb-logo {
width: 36px; height: 36px;
background: linear-gradient(135deg, var(--teal2), var(--teal));
border-radius: 10px;
width: 40px; height: 40px;
background: linear-gradient(45deg, #feda75, #fa7e1e, #d62976, #962fbf, #4f5bd5);
border-radius: 50%;
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.f-sb-name { color: var(--text-primary); font-weight: 900; font-size: 1.1rem; letter-spacing: -.3px; }
.f-sb-nav { padding: 12px 0; flex: 1; }
.f-sb-bottom { padding: 12px 0; border-top: 1px solid var(--border); }
.f-sb-logo img { filter: brightness(0) invert(1); width: 20px; height: 20px; }
.f-sb-name {
color: var(--text-primary); font-weight: 900; font-size: 1.15rem;
letter-spacing: -.4px;
}
.f-sb-nav { padding: 8px 8px; flex: 1; display: flex; flex-direction: column; gap: 4px; }
.f-sb-bottom { padding: 8px 8px 16px; border-top: 1px solid var(--border); }
.f-sb-item {
display: flex;
align-items: center;
gap: 13px;
gap: 14px;
width: 100%;
padding: 11px 20px;
padding: 10px 12px;
cursor: pointer;
transition: background .15s, color .15s;
color: var(--muted);
color: var(--text-primary);
font: inherit;
font-size: .9rem;
font-weight: 700;
font-size: .92rem;
font-weight: 500;
text-align: left;
border: none;
border-left: 3px solid transparent;
background: none;
border-radius: 10px;
}
.f-sb-item:hover { background: var(--hover-bg); color: var(--text-primary); }
.f-sb-item.active { background: rgba(var(--teal-rgb), .1); color: var(--teal); border-left-color: var(--teal); }
.f-sb-item:hover { background: var(--hover-bg); }
.f-sb-item.active { font-weight: 800; }
.f-sb-item svg { flex-shrink: 0; }
.f-sb-item.f-sb-create {
color: var(--teal);
}
.f-sb-item.f-sb-create svg { color: var(--teal); }
.f-sb-divider {
height: 1px; background: var(--border);
margin: 8px 0;
}
.f-right::-webkit-scrollbar { width: 3px; }
.f-right::-webkit-scrollbar-thumb { background: var(--muted2); border-radius: 2px; }
.f-right-label {
font-size: .65rem; font-weight: 900;
text-transform: uppercase; letter-spacing: .8px; color: var(--muted2);
}
.f-right-empty {
flex: 1; display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 12px; text-align: center;
border: 1.5px dashed var(--border);
border-radius: 16px; padding: 20px; min-height: 200px;
}
.f-right-empty-icon { font-size: 2rem; opacity: .2; }
.f-right-empty-text { color: var(--muted); font-size: .78rem; line-height: 1.5; }
.f-add-promo-btn {
background: rgba(var(--teal-rgb), .08); border: 1px solid rgba(var(--teal-rgb), .25);
border-radius: 10px; padding: 9px 14px;
color: var(--teal); font-family: var(--font);
font-size: .8rem; font-weight: 800; cursor: pointer;
transition: background .2s, transform .15s;
}
.f-add-promo-btn:hover { background: rgba(var(--teal-rgb), .15); transform: translateY(-1px); }
/* Unread badge (Instagram red) */
.f-sb-unread-badge {
background: #ff3040;
color: #fff;
font-size: .65rem;
font-weight: 800;
min-width: 18px;
height: 18px;
border-radius: 9px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 5px;
margin-left: auto;
}
/* ── Profile row at nav bottom ── */
.f-sb-profile {
display: flex; align-items: center; gap: 12px;
padding: 10px 12px; border-radius: 10px;
cursor: pointer; transition: background .15s;
width: 100%; border: none; background: none;
text-align: left; font-family: var(--font);
}
.f-sb-profile:hover { background: var(--hover-bg); }
.f-sb-profile .f-profile-avatar {
width: 32px; height: 32px;
border: 2px solid var(--border);
}
.f-sb-profile .f-profile-avatar-placeholder {
width: 32px; height: 32px; font-size: 1rem;
}
.f-sb-profile-info { flex: 1; min-width: 0; }
.f-sb-profile-name {
color: var(--text-primary); font-weight: 700; font-size: .85rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.f-sb-profile-login {
color: var(--muted); font-size: .72rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ── The drawer (mobile) ── */
.f-sidebar {
width: 260px; min-width: 260px;
background: var(--bg-surface);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
background: var(--bg-surface-solid);
border-right: 1px solid var(--border);
display: flex; flex-direction: column;
position: fixed; left: 0; top: 0; height: 100%; z-index: 50;
transform: translateX(-100%);
transition: transform .32s cubic-bezier(.4,0,.2,1);
position: fixed; left: 0; top: 0; height: 100%; z-index: 50;
}
.f-sidebar.open { transform: translateX(0); }
.f-right {
width: 220px; min-width: 220px; padding: 16px 12px;
border-left: 1px solid var(--border);
background: var(--bg-surface);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
display: flex; flex-direction: column; gap: 14px; overflow-y: auto;
@media (min-width: 900px) {
.f-sidebar {
position: relative;
transform: none;
width: 244px; min-width: 244px;
height: 100vh;
}
.f-overlay { display: none; }
}
/* ── Profile Component ── */
/* ── Right panel (suggestions, Instagram-style) ── */
.f-right {
width: 320px; min-width: 320px; padding: 24px 16px 24px 32px;
border-left: 1px solid transparent;
background: var(--bg-app);
display: flex; flex-direction: column; gap: 14px; overflow-y: auto;
}
.f-right::-webkit-scrollbar { width: 3px; }
.f-right::-webkit-scrollbar-thumb { background: var(--muted2); border-radius: 2px; }
.f-right-header {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 2px;
}
.f-right-label {
font-size: .8rem; font-weight: 700;
color: var(--muted);
}
.f-right-link {
font-size: .75rem; font-weight: 700; color: var(--text-primary);
background: none; border: none; cursor: pointer; font-family: var(--font);
transition: color .15s;
}
.f-right-link:hover { color: var(--muted); }
.f-right-profile {
display: flex; align-items: center; gap: 12px;
padding: 8px 0; cursor: pointer;
}
.f-right-profile-info { flex: 1; min-width: 0; }
.f-right-profile-name {
color: var(--text-primary); font-weight: 700; font-size: .86rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.f-right-profile-sub {
color: var(--muted); font-size: .74rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.f-right-action-btn {
background: none; border: none; cursor: pointer;
color: var(--teal); font-family: var(--font);
font-size: .78rem; font-weight: 700; padding: 6px 10px;
border-radius: 8px; transition: background .15s, color .15s;
white-space: nowrap;
}
.f-right-action-btn:hover { background: rgba(var(--teal-rgb), .1); }
/* Suggestion rows */
.f-ig-ring {
width: 40px; height: 40px; border-radius: 50%;
background: linear-gradient(45deg, #feda75, #fa7e1e, #d62976, #962fbf, #4f5bd5);
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.f-ig-ring-sm { width: 34px; height: 34px; }
.f-ig-avatar {
width: 34px; height: 34px; border-radius: 50%;
background: var(--bg-app);
display: flex; align-items: center; justify-content: center;
font-size: 1rem; overflow: hidden;
}
.f-ig-ring-sm .f-ig-avatar { width: 28px; height: 28px; font-size: .85rem; }
.f-ig-avatar img { width: 100%; height: 100%; object-fit: cover; }
.f-right-suggest {
display: flex; align-items: center; gap: 12px;
padding: 6px 0;
}
.f-right-suggest-info { flex: 1; min-width: 0; }
.f-right-suggest-name {
color: var(--text-primary); font-weight: 700; font-size: .86rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.f-right-suggest-sub {
color: var(--muted); font-size: .74rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Footer small links */
.f-right-foot {
display: flex; flex-wrap: wrap; gap: 4px 12px;
margin-top: 8px; padding-top: 12px; border-top: 1px solid var(--border);
}
.f-right-foot span {
color: var(--muted2); font-size: .68rem;
}
@media (max-width: 900px) {
.f-right {
display: none;
}
}
/* ── Profile Component (reused in modal header) ── */
.f-profile {
display: flex; align-items: center; gap: 12px;
padding: 10px 14px; border-radius: 12px;
@@ -93,20 +219,20 @@
border: 1px solid transparent;
}
.f-profile:hover {
background: rgba(45, 224, 200, .08);
border-color: rgba(45, 224, 200, .25);
background: rgba(var(--teal-rgb), .08);
border-color: rgba(var(--teal-rgb), .25);
}
.f-profile-avatar {
width: 40px; height: 40px;
border-radius: 50%; object-fit: cover;
flex-shrink: 0; border: 2px solid var(--teal);
flex-shrink: 0; border: 2px solid var(--border);
}
.f-profile-avatar-placeholder {
width: 40px; height: 40px;
border-radius: 50%; flex-shrink: 0;
background: rgba(45, 224, 200, .15);
border: 2px solid rgba(45, 224, 200, .3);
background: var(--bg-input);
border: 2px solid var(--border);
display: flex; align-items: center; justify-content: center;
font-size: 1.3rem;
}
@@ -120,9 +246,3 @@
color: var(--muted); font-size: .7rem;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 900px) {
.f-right {
display: none;
}
}

View File

@@ -1,78 +1,44 @@
/* ── Post Button (topbar CTA) ── */
/* ── Post Button (topbar CTA, Instagram blue) ── */
.f-post-btn {
display: flex; align-items: center; gap: 6px;
padding: 14px 18px; border-radius: 14px;
font-family: var(--font); font-weight: 900; font-size: 0.85rem; letter-spacing: 0.3px;
color: var(--teal);
background: rgba(var(--teal-rgb), 0.12); backdrop-filter: blur(14px);
border: 1px solid rgba(var(--teal-rgb), 0.25);
box-shadow: 0 4px 12px rgba(0,0,0,0.35), inset 0 0 6px rgba(var(--teal-rgb), 0.15), 0 0 9px rgba(var(--teal-rgb), 0.15);
cursor: pointer; transition: all 0.25s ease;
position: relative; overflow: hidden;
animation: postBtnGlow 3s ease-in-out infinite;
}
.f-post-btn::before {
content: ""; position: absolute; top: 0; left: 0;
width: 60%; height: 100%;
background: linear-gradient(105deg, transparent, rgba(255,255,255,0.5) 45%, rgba(255,255,255,0.15) 62%, transparent);
transform: skewX(-20deg) translateX(-150%);
pointer-events: none;
animation: postBtnShine 2.6s ease-in-out infinite;
padding: 8px 16px; border-radius: 999px;
font-family: var(--font); font-weight: 700; font-size: 0.82rem;
color: #fff;
background: var(--teal);
border: 1px solid transparent;
box-shadow: 0 2px 8px rgba(var(--teal-rgb), .3);
cursor: pointer; transition: all 0.2s ease;
white-space: nowrap;
}
.f-post-btn:hover {
transform: translateY(-2px) scale(1.02);
border-color: rgba(var(--teal-rgb), 0.5);
box-shadow: 0 6px 16px rgba(0,0,0,0.45), inset 0 0 9px rgba(var(--teal-rgb), 0.25), 0 0 13px rgba(var(--teal-rgb), 0.35);
background: var(--teal2);
transform: translateY(-1px);
}
.f-post-btn:active {
transform: translateY(0) scale(0.98);
box-shadow: 0 2px 6px rgba(0,0,0,0.4), inset 0 0 5px rgba(var(--teal-rgb), 0.2);
}
@keyframes postBtnShine {
0% { transform: skewX(-20deg) translateX(-150%); }
55% { transform: skewX(-20deg) translateX(250%); }
100% { transform: skewX(-20deg) translateX(250%); }
}
@keyframes postBtnGlow {
0%, 100% { box-shadow: 0 4px 12px rgba(0,0,0,0.35), inset 0 0 6px rgba(var(--teal-rgb), 0.15), 0 0 8px rgba(var(--teal-rgb), 0.25); }
50% { box-shadow: 0 4px 12px rgba(0,0,0,0.35), inset 0 0 12px rgba(var(--teal-rgb), 0.3), 0 0 18px rgba(var(--teal-rgb), 0.5); }
}
/* ── Login Button (topbar) ── */
.f-login-btn {
display: flex; align-items: center; gap: 6px;
padding: 8px 18px; border-radius: 14px;
font-family: var(--font); font-weight: 800; font-size: 0.85rem; letter-spacing: 0.3px;
color: var(--teal);
background: rgba(var(--teal-rgb), 0.12); backdrop-filter: blur(14px);
border: 1px solid rgba(var(--teal-rgb), 0.25);
box-shadow: 0 6px 18px rgba(0,0,0,0.35), inset 0 0 10px rgba(var(--teal-rgb), 0.12), 0 0 14px rgba(var(--teal-rgb), 0.12);
cursor: pointer; transition: all 0.25s ease;
position: relative; overflow: hidden; white-space: nowrap;
}
.f-login-btn::before {
content: ""; position: absolute; inset: 0;
background: linear-gradient(120deg, rgba(255,255,255,0.25), rgba(255,255,255,0.05) 40%, transparent 60%);
opacity: 0.4; pointer-events: none;
}
.f-login-btn:hover {
transform: translateY(-2px) scale(1.02);
border-color: rgba(var(--teal-rgb), 0.5);
box-shadow: 0 10px 26px rgba(0,0,0,0.45), inset 0 0 14px rgba(var(--teal-rgb), 0.22), 0 0 20px rgba(var(--teal-rgb), 0.3);
}
.f-login-btn:active {
transform: translateY(0) scale(0.98);
box-shadow: 0 3px 10px rgba(0,0,0,0.4), inset 0 0 8px rgba(var(--teal-rgb), 0.18);
}
.f-post-btn:active { transform: translateY(0) scale(.98); }
.f-post-btn span {
display: flex; align-items: center; justify-content: center;
width: 16px; height: 16px; border-radius: 50%;
background: rgba(var(--teal-rgb), 0.2); box-shadow: 0 0 6px rgba(var(--teal-rgb), 0.4);
background: rgba(255,255,255,.25);
font-size: 0.9rem; transform: translateY(-1px);
}
/* ── Login Button (topbar) ── */
.f-login-btn {
display: flex; align-items: center; gap: 6px;
padding: 8px 18px; border-radius: 999px;
font-family: var(--font); font-weight: 700; font-size: 0.85rem;
color: var(--teal);
background: var(--bg-input);
border: 1px solid var(--border);
cursor: pointer; transition: all 0.2s ease;
white-space: nowrap;
}
.f-login-btn:hover {
background: rgba(var(--teal-rgb), .1);
border-color: rgba(var(--teal-rgb), .4);
}
.f-login-btn:active { transform: translateY(0) scale(.98); }
/* ── Feed Empty / Error States ── */
.f-center {
flex: 1; display: flex; flex-direction: column;
@@ -91,6 +57,7 @@
border-radius: 50%; animation: spin .7s linear infinite; flex-shrink: 0;
}
/* ── Toast ── */
.f-toast {
position: fixed;
bottom: 24px;
@@ -100,23 +67,15 @@
align-items: center;
gap: 10px;
padding: 12px 16px;
border-radius: 8px;
border-radius: 12px;
font-size: .9rem;
color: #fff;
box-shadow: 0 4px 16px rgba(0, 0, 0, .3);
box-shadow: 0 4px 16px rgba(0, 0, 0, .25);
z-index: 1000;
animation: f-toast-in .2s ease-out;
}
.f-toast-error {
background: #e5484d;
}
.f-toast-success {
background: #2de0c8;
color: #0a0a0a;
}
.f-toast-error { background: #ed4956; }
.f-toast-success { background: #0095f6; }
.f-toast-close {
background: none;
border: none;
@@ -126,7 +85,6 @@
line-height: 1;
padding: 0;
}
@keyframes f-toast-in {
from { opacity: 0; transform: translate(-50%, 8px); }
to { opacity: 1; transform: translate(-50%, 0); }