SHA256
Скорректировать main к базе 553a1f1 и UI из Pixel
This commit is contained in:
@@ -45,12 +45,10 @@
|
||||
- **Обычные линии:** SVG `<path> Q` (квадратичные Безье) — тонкие матовые дуги (`stroke-width ~1.0–1.2`),
|
||||
градиент с глубоким уходом в прозрачность: неон-центр `0.42` → цвет роли `0.07` у аватарки (растворяются
|
||||
в фоне, не спорят с сияющими). Изгиб реагирует на скорость.
|
||||
- **Сияющие связи (плазменный композитинг):** один центральный S-путь (cubic Bézier) и три наложенных
|
||||
слоя с одинаковым `d`: `.fg-plasma-flare` (16px, `#00bfff`, blur6), `.fg-plasma-tube` (6px, `#00e5ff`,
|
||||
blur2) и `.fg-plasma-core` (2px, `#dffaff`). Поле и трубка идут в `mix-blend-mode: screen`, поэтому
|
||||
свечение складывается аддитивно с тёмным фоном и ярче проявляется в пересечениях у центра.
|
||||
- **Обычные связи:** теперь это мягкое цветное свечение по типу связи (семья/друзья/бизнес/контакт) —
|
||||
широкая полупрозрачная подложка плюс тонкое ядро, без SVG-blur.
|
||||
- **Сияющие связи (двухслойный «световод», Neon Layering):** два пути на одну связь — широкий размытый
|
||||
GLOW (`stroke-width 4`, неон, `filter: blur(2px)`, `opacity 0.4`) + тонкий чёткий CORE (`1.5px`,
|
||||
`#e0f7fc`). Линия остаётся изящной, но обретает объёмное OLED-свечение; растут оба синхронно (общий
|
||||
dashoffset). Никаких бегущих импульсов.
|
||||
- **Жесты:** свайп-pan с инерцией (новое касание прерывает); короткий тап — центрирование + нижний
|
||||
сниппет; долгий тап — контекстное меню (в `#modal-root`, позиция по `getBoundingClientRect`); тап
|
||||
по центру — профиль. Нажатия на чипы фильтров гасят `pointerdown` (stopPropagation), чтобы сцена не
|
||||
@@ -63,7 +61,7 @@
|
||||
(`hue-rotate`). На компоновщике (GPU), не будит rAF; подписи имён остаются контрастными.
|
||||
- **Стеклянные чипы фильтров (frosted glass):** `background: rgba(255,255,255,0.03)`,
|
||||
`backdrop-filter: blur(12px)`, граница `0.5px solid rgba(255,255,255,0.1)`; активный — подсвечен сине-голубым.
|
||||
- **Поллиш:** «прицел» в центре (+пульс при захвате фокуса); «дыхание» фокуса (бесконечная CSS-анимация
|
||||
- **Поллиш:** «дыхание» фокуса (бесконечная CSS-анимация
|
||||
размера, GPU, не будит rAF); свечение «сияющих» узлов — мягкая медленная пульсация (3.6с) многослойной
|
||||
`box-shadow` + размытый ореол через SVG-фильтр `#fg-shine-glow`; тестовые фото-аватарки (`NETWORK_PHOTOS`);
|
||||
хард-лимит ~90 DOM-аватарок (остальное — SVG-точки).
|
||||
@@ -90,6 +88,94 @@
|
||||
тап по узлам переключает сети.
|
||||
- Реальный путь (`/network-view`) требует живого `wss://shineup.me/ws` (локально — `ws_open_error`, это норма).
|
||||
|
||||
## Режим «Интерактивная паутина» (ветка `pixel-web`, эксперимент, только лаборатория)
|
||||
Включается чипом «🌌 Вселенная». Дальние уровни (2-3) по умолчанию скрыты и раскрываются локально:
|
||||
- **Hover-превью (наведение):** навёл мышь/палец на узел — его ветка временно выплывает; убрал —
|
||||
втягивается. Реализация: `pointerover/out` (мышь) и `pointerdown/up` (палец) → `onNodeHover` →
|
||||
`graph.setHover(node|null)`; узел получает флаг `hovered`.
|
||||
- **Фиксация кликом (pin):** тап/клик по узлу → `graph.toggleExpand` ставит флаг `pinned` — ветка
|
||||
остаётся раскрытой и после ухода курсора. Повторный клик по раскрытому узлу **сворачивает** его
|
||||
(надёжный toggle: `isOpen = pinned || expandP>0.5` → сброс `pinned`+`hovered`).
|
||||
- Эффективное раскрытие = `pinned || hovered` (см. `expandTargetOf`), прогресс `expandP` (~400мс).
|
||||
- **Spotlight-затемнение:** пока есть закреплённая ветка, остальные тускнеют до `SPOTLIGHT_DIM=0.25`
|
||||
(узлы и их линии), фокус и закреплённая/наведённая ветка — 100%. Плавно через `spotCur` (lerp).
|
||||
- **Узлы 2-го уровня — полноценные аватарки:** фото-лицо (pravatar) + имя, `DEEP2_SCALE=0.62`
|
||||
(≈radius 16px), `DEEP2_OPACITY=0.85`. Не «пустые кружки», а видимые друзья друзей.
|
||||
- **Глобальный сброс:** тап по корню (Иван) → `collapseAll()` снимает `pinned`/`hovered` → 100% яркость.
|
||||
- **Адаптивное расталкивание (collision):** раскрытая ветка усиливает отталкивание соседних узлов
|
||||
1-го уровня пропорционально `expandP` (`EXPAND_REPULSION=2.4`) — кластеры разъезжаются, не накладываясь.
|
||||
- **Камера-доводчик:** при фиксации ветки, если её «веер» упирается в край экрана, камера мягко
|
||||
дотягивается (`glideCameraTo` → `camTargetX/Y`, lerp `CAM_GLIDE_K` в tick). Любой жест отменяет доводчик.
|
||||
- **Свободный зум:** колесо мыши (`onWheel`) и щипок двумя пальцами (`activePointers`/`pinching`) —
|
||||
масштаб `zoom` (0.55–2.6), «к точке» под курсором/центром щипка; мир масштабируется CSS-`scale`,
|
||||
линии (отдельный SVG) пересчитываются в экранных координатах (× `zoom`).
|
||||
- **Синхро-пульс линий:** сияющие/трековые «световоды» (`.fg-edge-glow`/`.fg-edge-core`) «дышат»
|
||||
толщиной/размытием 3.6с — в такт ободку сияющего узла (в покое SVG не перерисовывается → синхронно).
|
||||
- Мерцающие микрозвёзды 3-го уровня (`fg-star-twinkle`), хаптика (`navigator.vibrate`) на нажатие/раскрытие/натяжение.
|
||||
|
||||
### Умный фокус (Smart Zoom / «аквариум») — ветка `pixel-aquarium`
|
||||
**Наведение** (hover/палец) на узел — лёгкое превью ветки (раскрытие на месте, без камеры).
|
||||
**Клик/тап по ЛЮБОМУ узлу** — **погружение (dive)** с кинематографичным наездом:
|
||||
- **Камера-полёт + зум** (`diveTo` → `diveTargetId`/`diveZoom=1.7`, лёт в `tick` с `DIVE_FLY_K` ≈600мс):
|
||||
узел плавно центрируется (offset ~0) и **вырастает до единого видимого размера** `HERO_VISUAL=1.4`
|
||||
независимо от уровня (`depthScale = HERO_VISUAL / baseScaleOf`); его прямые дети — до `DIVE_CHILD_VISUAL`.
|
||||
- **Адаптивный радиус орбиты (фикс слипания):** дети раскладываются на кольце
|
||||
`ringR = max(baseR + радиус_родителя, число_детей × 13)` — НЕ лезут на (увеличенный зумом) родитель
|
||||
и друг на друга (проверено: мин. дистанция 125px, 0 наложений). Радиус растёт вместе с зумом родителя.
|
||||
- **Глубина «аквариума»** (`contextTargetOf` → `depthScale`/`depthBlur`/`spotCur`, лерп): Иван и боковые
|
||||
ветки **уменьшаются** (root ×0.55, фон ×0.55) + уходят в **blur 3px** + тускнеют до 0.25 → задний план.
|
||||
- **Железный Spotlight (единый активный путь):** `diveTo` сначала гасит ВСЕ прежние pin/hover, затем
|
||||
раскрывает только путь к новой цели. Открыто → путь Иван→…→узел = 1.0, остальное = 0.25; переключение
|
||||
веток сбрасывает прежнюю; **выход/`exitDive`/тап по Ивану → ВСЕ узлы гарантированно 1.0 + камера отъезжает**.
|
||||
- **Нить-крошка**: путь (`divePathSet`/`onPath`) горит ярким «световодом» — виден путь назад к Ивану.
|
||||
- **Pinch-to-Zoom + LOD**: щипок/колесо меняют `zoom`; при `zoom ≥ LOD_ZOOM (1.55)` видимые точки 3-го
|
||||
уровня **дорисовываются как аватарки** (`updateLod`/`setNodeLod`), при отдалении — обратно в точки.
|
||||
- Глубина — фейк-3D через масштаб + CSS-`blur` (GPU), без WebGL.
|
||||
|
||||
**Полиш (партия 1):** веер детей раскрывается **полукругом «наружу»** (от пути назад, `DEEP_FAN`,
|
||||
по `sibIndex`) — не перекрывает нить-крошку; **LOD с гистерезисом** (`LOD_ZOOM_UP=1.6`/`DOWN=1.4` — без
|
||||
мигания у порога); **двойной тап по фону** и **сильный pinch-out на мин. зуме** = быстрый выход;
|
||||
**префетч аватарок** детей при наведении/нырке.
|
||||
|
||||
**Фишки (партия 2, лаборатория):**
|
||||
- **Поиск + телепорт** — строка `.fg-search`; Enter → `graph.findNode(имя)` → камера летит к узлу (dive в
|
||||
«Вселенной», иначе перецентр).
|
||||
- **Хлебные крошки** — `.fg-breadcrumb` «Иван › Нина › Ада» (движок шлёт `onDiveChange(path)`,
|
||||
API `getDivePath()`); клик по корню — полный сброс, по предку — навигация на тот уровень.
|
||||
- **Бейдж числа связей** — `.fg-node-badge` (число из `degreeById`, обновляется в `updateBadges`).
|
||||
- **Цветовые кластеры** — мягкая аура узла по типу связи (CSS `is-family/friend/business/contact`).
|
||||
|
||||
**Линии-«жгуты» (партия 4, по референсу — плазменный композитинг):**
|
||||
- **Сияющие** — ОДИН центральный S-путь (cubic Bézier) + ТРИ наложенных слоя с ОДИНАКОВЫМ `d` (объём
|
||||
из толщины+размытия, НЕ из геометрии — никаких расходящихся линий):
|
||||
Настоящий НЕОН (видимый ореол вокруг яркого ядра; поле/трубка в `mix-blend-mode: screen` — свет
|
||||
складывается аддитивно с тёмным фоном, а в пересечениях у центра ярче — энергохаб):
|
||||
- `.fg-plasma-flare` — плазменное облако: 16px, `#00bfff`, opacity 0.42, **`feGaussianBlur` stdDev=6**, screen (+ «дыхание» 3.6с);
|
||||
- `.fg-plasma-tube` — направляющий свет: 6px, `#00e5ff`, opacity 0.85, **`feGaussianBlur` stdDev=2**, screen;
|
||||
- `.fg-plasma-core` — ядро: 2px, `#dffaff` (светло-голубо-белое), opacity 1, без размытия.
|
||||
Толщина/насыщенность подогнаны под референс (толстая яркая голубая плазма, гладкие края).
|
||||
S-волна спокойная/изящная (amp до 13px). Размытие — именно SVG-фильтры (`#fg-plasma-blur6/2`), т.к.
|
||||
CSS-`filter` на `<path>` в части мобильных WebView не применяется (отсюда был «плоский»/«канатный» вид).
|
||||
⚠️ Это НЕ Canvas-движок (не библиотека force-graph): связи — реальные SVG `<path>`, фильтры применяются.
|
||||
Прозрачность слоёв inline (× spotlight/глубину). Тяжёлый blur только у сияющих (их мало) — перф.
|
||||
- **Не-сияущие** — мягкое свечение **в цвете связи** (семья/друзья/бизнес/контакт): широкая
|
||||
полупрозрачная подложка + тонкое ядро, без SVG-blur (дёшево). «Похоже, но тише».
|
||||
|
||||
**Фишки (партия 3, лаборатория):**
|
||||
- **Общие связи** — среди друзей человека один помечен как «общий» (он и твой друг тоже): золотой
|
||||
ободок + ★ (CSS `is-common`; в лаб-генерации `addDeepLevels` подставляет узнаваемого друга Ивана).
|
||||
- **Доступность** — визуально скрытый (`sr-only`) текстовый список графа `.fg-a11y` (центр + связи
|
||||
1-го уровня) для скринридеров; обновляется в `updateA11y` при перестроении. Полезно и для реального пути.
|
||||
|
||||
**Автопроверки (`?fgtest`):** `js/pages/network/selftest.js` автозапускается в лаборатории при `?fgtest`,
|
||||
прогоняет 17 ассертов (центровка/collision/полукруг/spotlight/переключение/LOD/поиск/крошки/бейдж/выход) через
|
||||
детерминированные dev-хелперы движка `graph.debugState()` и `graph.pumpForTest()` (синхронно докручивают кадры
|
||||
до покоя — не зависят от троттлинга rAF). Результат → консоль и `window.__fgTestResults`. В обычной работе не активны.
|
||||
|
||||
> ⚠️ Эксперименты на ветках `pixel-web` (паутина) и `pixel-aquarium` (Smart Zoom) — для отката.
|
||||
> Реальный путь `/network-view` не затронут: deep-код под `tier ≥ 2` / `hasDeep`, dive — только tier≥2
|
||||
> (в реальном пути их нет), depthScale/Blur по умолчанию нейтральны, `updateLod` выходит при `!hasDeep`.
|
||||
|
||||
## Ограничения / на будущее
|
||||
- Многоуровневая глубина (друзья друзей мельче, 3-й уровень — точки), кластеры, «общие связи»
|
||||
упираются в API (отдаёт только прямые связи) — требуют доработки сервера.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,34 +60,39 @@
|
||||
transition: opacity 420ms ease; /* плавное появление линий при перестройке */
|
||||
}
|
||||
|
||||
/* Сияющая связь = плазменный композитинг (3 слоя на одном S-пути, см. renderEdges). */
|
||||
.fg-plasma-flare {
|
||||
/* Сияющая связь = плазменный композитинг (3 слоя на ОДНОМ S-пути, см. renderEdges).
|
||||
Настоящий НЕОН: яркое светлое ядро + ВИДИМЫЙ голубой ореол вокруг. Слои поля/трубки идут в режиме
|
||||
mix-blend-mode: screen → свет складывается аддитивно с тёмным фоном (как реальное свечение), а в точках
|
||||
пересечения нитей у центра — ярче (энергетический хаб). Прозрачность слоёв — inline (×spotlight/глубину). */
|
||||
.fg-plasma-flare { /* нижний: широкое насыщенное голубое плазменное свечение (по референсу) */
|
||||
fill: none;
|
||||
stroke: #00bfff;
|
||||
stroke: #00bfff; /* глубокий голубой */
|
||||
stroke-width: 16;
|
||||
stroke-linecap: round;
|
||||
filter: url(#fg-plasma-blur6);
|
||||
mix-blend-mode: screen;
|
||||
filter: url(#fg-plasma-blur6); /* мягкое объёмное свечение (гладкие края — как на референсе) */
|
||||
mix-blend-mode: screen; /* аддитивное свечение поверх тёмного фона */
|
||||
/* синхро-«дыхание» поля толщиной в такт ободку сияющего узла (3.6с); прозрачность не трогаем (inline) */
|
||||
animation: fg-plasma-breath 3.6s ease-in-out infinite;
|
||||
}
|
||||
.fg-plasma-tube {
|
||||
.fg-plasma-tube { /* средний: яркая толстая неоновая трубка */
|
||||
fill: none;
|
||||
stroke: #00e5ff;
|
||||
stroke: #00e5ff; /* яркий циан */
|
||||
stroke-width: 6;
|
||||
stroke-linecap: round;
|
||||
filter: url(#fg-plasma-blur2);
|
||||
mix-blend-mode: screen;
|
||||
filter: url(#fg-plasma-blur2); /* SVG feGaussianBlur stdDeviation=2 */
|
||||
mix-blend-mode: screen; /* аддитивное свечение */
|
||||
}
|
||||
.fg-plasma-core {
|
||||
.fg-plasma-core { /* верхний: яркое чёткое ядро (светло-голубо-белое) */
|
||||
fill: none;
|
||||
stroke: #dffaff;
|
||||
stroke: #dffaff; /* светло-голубо-белое — «жидкое» ядро */
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
/* мягкое «дыхание» плазменного облака толщиной, синхронно с пульсом сияющего ободка (3.6с) */
|
||||
@keyframes fg-plasma-breath {
|
||||
0%, 100% { stroke-width: 14; }
|
||||
50% { stroke-width: 19; }
|
||||
50% { stroke-width: 19; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
@@ -120,7 +125,7 @@
|
||||
height: 52px;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
transition: box-shadow 160ms ease, border-color 160ms ease;
|
||||
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
|
||||
}
|
||||
|
||||
.fg-node.is-family .node-dot {
|
||||
@@ -150,10 +155,18 @@
|
||||
box-shadow: 0 0 0 2px rgba(143, 231, 255, 0.45), 0 10px 22px rgba(4, 8, 15, 0.45);
|
||||
}
|
||||
|
||||
/* Тактильный отклик «нажатия вглубь»: аватарка слегка вдавливается (scale 0.92), а неоновое кольцо
|
||||
вспыхивает заметно ярче (~1.5×). Срабатывает при наведении, фокусе и зажатии (.is-pressed). */
|
||||
.fg-node:focus-visible .node-dot,
|
||||
.fg-node:hover .node-dot {
|
||||
border-color: rgba(166, 218, 255, 0.95);
|
||||
box-shadow: 0 0 0 3px rgba(77, 160, 255, 0.22), 0 8px 16px rgba(4, 8, 15, 0.35);
|
||||
.fg-node:hover .node-dot,
|
||||
.fg-node.is-pressed .node-dot {
|
||||
transform: scale(0.92);
|
||||
border-color: rgba(160, 240, 255, 0.95);
|
||||
box-shadow: 0 0 0 2px rgba(150, 238, 255, 0.6), 0 0 22px rgba(120, 230, 255, 0.85);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fg-node.is-pressed .node-dot { transform: none; }
|
||||
}
|
||||
|
||||
/* «Сияние» — мягкое живое свечение НА УЗЛЕ (аватарке), а не на линии связи.
|
||||
@@ -283,28 +296,50 @@
|
||||
.fg-dot.is-contact { background: #36435c; }
|
||||
.fg-dot.is-shine { box-shadow: 0 0 9px rgba(130, 235, 255, 0.75); }
|
||||
|
||||
/* «Прицел» в центре экрана (зона фокуса) — позади узлов */
|
||||
.fg-reticle {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: -32px 0 0 -32px;
|
||||
border-radius: 50%;
|
||||
border: 2px dashed rgba(150, 190, 255, 0.3);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: 0.45;
|
||||
transition: width 200ms ease, height 200ms ease, margin 200ms ease, border-color 200ms ease, opacity 200ms ease;
|
||||
/* === Глубина 2-3 уровней (прототип «Вселенная», только лаборатория) ============== */
|
||||
/* 2-й уровень — «друзья друзей»: полноценная аватарка (лицо + имя), просто мельче основных.
|
||||
Масштаб/прозрачность задаёт движок; здесь — читаемый ободок и подпись (не «дырка»). */
|
||||
.fg-node.is-tier2 .node-dot {
|
||||
border-color: rgba(170, 200, 240, 0.65);
|
||||
box-shadow: 0 2px 8px rgba(4, 8, 15, 0.4);
|
||||
}
|
||||
.fg-node.is-tier2 .fg-node-label {
|
||||
font-size: 9px;
|
||||
opacity: 0.9;
|
||||
top: calc(100% + 1px);
|
||||
}
|
||||
|
||||
.fg-reticle.is-locked {
|
||||
width: 94px;
|
||||
height: 94px;
|
||||
margin: -47px 0 0 -47px;
|
||||
border-color: rgba(130, 235, 255, 0.65);
|
||||
opacity: 0.85;
|
||||
/* 3-й уровень — микрозвезда: светящаяся точка без картинки (эффект далёкого созвездия). */
|
||||
.fg-dot.is-tier3 {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border: 0;
|
||||
background: radial-gradient(circle, #e6f6ff 0%, rgba(150, 215, 255, 0.95) 38%, rgba(120, 200, 255, 0) 75%);
|
||||
box-shadow: 0 0 6px rgba(150, 220, 255, 0.9), 0 0 13px rgba(115, 200, 255, 0.5);
|
||||
/* медленное мерцание «звезды» — по box-shadow/яркости (НЕ opacity/scale: ими управляет движок при
|
||||
раскрытии). У каждой звезды своя задержка (inline animation-delay) → живое созвездие. */
|
||||
animation: fg-star-twinkle 3.4s ease-in-out infinite;
|
||||
}
|
||||
.fg-dot.is-tier3.is-shine {
|
||||
background: radial-gradient(circle, #ffffff 0%, rgba(160, 240, 255, 1) 38%, rgba(120, 230, 255, 0) 75%);
|
||||
box-shadow: 0 0 8px rgba(160, 240, 255, 1), 0 0 16px rgba(115, 220, 255, 0.7);
|
||||
}
|
||||
|
||||
@keyframes fg-star-twinkle {
|
||||
0%, 100% { box-shadow: 0 0 3px rgba(150, 220, 255, 0.45), 0 0 7px rgba(115, 200, 255, 0.25); filter: brightness(0.78); }
|
||||
50% { box-shadow: 0 0 7px rgba(165, 235, 255, 0.95), 0 0 15px rgba(120, 210, 255, 0.6); filter: brightness(1.3); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fg-dot.is-tier3 { animation: none; }
|
||||
}
|
||||
|
||||
/* Чип-переключатель «Вселенная» — активное состояние наследует стиль .fg-filter-chip.is-active */
|
||||
.fg-deep-chip.is-active {
|
||||
background: rgba(150, 130, 255, 0.18);
|
||||
border-color: rgba(190, 170, 255, 0.6);
|
||||
box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.12), 0 0 14px rgba(150, 120, 255, 0.3);
|
||||
color: #efeaff;
|
||||
}
|
||||
|
||||
/* «Призрак» старой карты при Z-переходе (эффект погружения) */
|
||||
@@ -319,17 +354,6 @@
|
||||
transition: transform 1000ms cubic-bezier(0.16, 1, 0.3, 1), opacity 1000ms ease;
|
||||
}
|
||||
|
||||
/* Импульс центрального кольца при захвате нового фокуса */
|
||||
.fg-reticle.is-pulse {
|
||||
animation: fg-reticle-pulse 0.6s ease;
|
||||
}
|
||||
|
||||
@keyframes fg-reticle-pulse {
|
||||
0% { transform: scale(1); }
|
||||
40% { transform: scale(1.22); border-color: rgba(130, 235, 255, 0.9); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Панель фильтров слоёв (оверлей под шапкой) */
|
||||
.fg-filter-bar {
|
||||
position: absolute;
|
||||
@@ -514,3 +538,141 @@
|
||||
.fg-sheet-actions > button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* === Партия 2: бейдж-счётчик связей, поиск, хлебные крошки, цветовые кластеры ============ */
|
||||
|
||||
/* Бейдж числа связей — маленькая пилюля в правом-верхнем углу аватарки */
|
||||
.fg-node-badge {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: -2px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
border-radius: 999px;
|
||||
background: rgba(16, 24, 40, 0.92);
|
||||
border: 1px solid rgba(150, 200, 255, 0.5);
|
||||
color: #d9ecff;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.fg-node.is-focus .fg-node-badge {
|
||||
background: rgba(61, 196, 223, 0.95);
|
||||
border-color: rgba(220, 245, 255, 0.8);
|
||||
color: #06131c;
|
||||
}
|
||||
.fg-node.is-tier2 .fg-node-badge { transform: scale(0.85); }
|
||||
|
||||
/* Цветовые кластеры: мягкая аура узла по типу связи (визуально группирует «семью/друзей/бизнес») */
|
||||
.fg-node.is-family .node-dot { box-shadow: 0 0 16px rgba(255, 159, 94, 0.20); }
|
||||
.fg-node.is-friend .node-dot { box-shadow: 0 0 16px rgba(120, 179, 255, 0.20); }
|
||||
.fg-node.is-business .node-dot { box-shadow: 0 0 16px rgba(190, 150, 255, 0.20); }
|
||||
.fg-node.is-contact .node-dot { box-shadow: 0 0 16px rgba(170, 190, 220, 0.16); }
|
||||
/* сияющие/фокус — свой эффект свечения (см. выше), ауру кластера не навязываем */
|
||||
.fg-node.is-shine .node-dot, .fg-node.is-focus .node-dot { box-shadow: none; }
|
||||
|
||||
/* Строка поиска (оверлей вверху, под панелью фильтров) */
|
||||
.fg-search {
|
||||
position: absolute;
|
||||
top: max(92px, calc(env(safe-area-inset-top) + 88px));
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 12;
|
||||
width: min(280px, 70vw);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 0.5px solid rgba(255, 255, 255, 0.12);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.fg-search input {
|
||||
flex: 1;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #eaf2ff;
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
}
|
||||
.fg-search input::placeholder { color: #7d8aa6; }
|
||||
.fg-search .fg-search-ico { color: #9fc0ff; font-size: 13px; }
|
||||
|
||||
/* Хлебные крошки навигации (стек погружений: Иван › Нина › Ада) */
|
||||
.fg-breadcrumb {
|
||||
position: absolute;
|
||||
top: max(132px, calc(env(safe-area-inset-top) + 128px));
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 12;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
padding: 0 12px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.fg-breadcrumb.is-open { display: flex; }
|
||||
.fg-crumb {
|
||||
pointer-events: auto;
|
||||
border: 0;
|
||||
background: rgba(16, 24, 40, 0.7);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
color: #cfe0ff;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
max-width: 110px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.fg-crumb.is-last {
|
||||
background: rgba(125, 215, 255, 0.18);
|
||||
color: #eaf7ff;
|
||||
cursor: default;
|
||||
}
|
||||
.fg-crumb-sep { color: #5f7196; font-size: 11px; align-self: center; pointer-events: none; }
|
||||
|
||||
/* Доступность: визуально скрытый список графа для скринридеров (sr-only, читается ассистивными технологиями) */
|
||||
.fg-a11y {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* «Общая связь» (этот человек — и твой друг тоже): золотой ободок + ★ под аватаркой */
|
||||
.fg-node.is-common .node-dot {
|
||||
border-color: rgba(255, 214, 120, 0.95);
|
||||
box-shadow: 0 0 14px rgba(255, 200, 90, 0.4);
|
||||
}
|
||||
.fg-node.is-common .node-dot::after {
|
||||
content: '★';
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
color: #ffd678;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user