Анализ сайта dash.trueoriginal.com
Основное Готовность: 50%
Домен
dash.trueoriginal.com
Состояние доменного имени
?
Проверяем корректность доменного имени и наличие технических проблем на уровне домена.
Используйте для продвижения только домен второго уровня.
Длина домена велика. Но если вы продвигаете запрос, входящий в название домена, то это хорошо.
Ответ сервера
200 Успешный ответ
HTTP-код ответа и цепочка редиректов
?
Код 200 — страница доступна. Коды 3xx — редиректы (цепочки замедляют загрузку и размывают ссылочный вес). Коды 4xx/5xx — ошибки, поисковик не сможет проиндексировать страницу.
Кол-во редиректов 3 слишком большое! Проверьте настройки сайта и веб-сервера!
Цепочка редиректов:
http://dash.trueoriginal.com
301 MovedPermanently
https://dash.trueoriginal.com/
302 Found
https://dash.trueoriginal.com/sign-in/?r=%2F
200 OK
Безопасность
Сайт безопасен
Использование HTTPS и SSL-сертификат
?
HTTPS — обязательный стандарт. Google и Яндекс отдают предпочтение защищённым сайтам. Отсутствие SSL или просроченный сертификат ведут к предупреждениям в браузере и снижению позиций.
Не настроен HSTS (Strict-Transport-Security) — рекомендуется включить.
На сайте работает защищенный протокол ssl и сайт открывается по https.
Ssl-сертификат действителен до 09.01.2027 2:59:59.
HTTP автоматически перенаправляется на HTTPS.
Поздравляем! Сайт не содержится в реестре РКН.
Кодировка
utf-8
Кодировка символов страницы
?
Стандарт — UTF-8. Неправильная кодировка вызывает нечитаемые символы и мешает поисковику корректно распознать текст страницы.
Указана кодировка на странице utf-8.
Язык
en
Атрибут lang в HTML-теге
?
Атрибут lang (<html lang="ru">) сообщает поисковикам и браузерам, на каком языке написана страница. Помогает при ранжировании в региональном поиске.
Язык документа указан явно: en.
Скорость загрузки
~0,91сек
Время отклика сервера (TTFB)
?
Time To First Byte — время до получения первого байта от сервера. Норма до 200 мс. Медленный отклик ухудшает пользовательский опыт и ранжирование: Яндекс и Google учитывают скорость страниц.
Скорость загрузки сайта 0,91сек оптимальна.
Объем документа
90Кб
Размер HTML-кода страницы
?
Слишком большой HTML замедляет парсинг браузером и сканирование поисковым роботом. Рекомендуется не более 200 Кб.
Объем html-документа 90Кб оптимален.
Структура html-документа корректна.
Ресурсы
Ресурсы: 64
Внешние ресурсы страницы (CSS, JS, изображения)
?
Количество и тип подключённых ресурсов влияют на скорость загрузки. Большое число запросов увеличивает время рендеринга страницы.
Кол-во файлов ресурсов 64 много для одной страницы. Приемлемо до 10. Проведите оптимизацию файлов ресурсов!
Показать полный список ресурсов
| Тип | Название | Значение |
|---|---|---|
| stylesheet | /site/_codebase/assets/js/plugins/select2/css/select2.min.css | |
| stylesheet | /site/_codebase/assets/js/plugins/datatables/dataTables.bootstrap4.css | |
| stylesheet | /site/_codebase/assets/js/plugins/slick/slick.css | |
| stylesheet | /site/_codebase/assets/js/plugins/slick/slick-theme.css | |
| stylesheet | https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500&display=swap | |
| stylesheet | https://cdn.truecdn.io/fonts/true/style.css | |
| stylesheet | /site/_codebase/assets/css/codebase.css?v=1787322557 | |
| stylesheet | /site/_codebase/assets/css/true.css?v=1787322557 | |
| stylesheet | /site/_codebase/assets/css/helpers.css?v=1787322557 | |
| stylesheet | /site/_codebase/assets/css/ux.css?v=1787322557 | |
| stylesheet | /site/_codebase/assets/css/switch.css?v=1787322557 | |
| stylesheet | /site/_codebase/assets/css/dark-mode.css?v=1787322557 | |
| stylesheet | https://fonts.googleapis.com/css2?family=Marcellus&display=swap | |
| stylesheet | /site/_codebase/assets/js/plugins/magnific-popup/magnific-popup.css | |
| stylesheet | //static.trueoriginal.com/site/_codebase/assets/js/plugins/highlightjs/styles/github-gist.css | |
| stylesheet | //static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-datepicker/css/bootstrap-datepicker3.min.css | |
| stylesheet | //static.trueoriginal.com/site/_codebase/assets/js/plugins/flatpickr/flatpickr.min.css | |
| stylesheet | //static.trueoriginal.com/site/_codebase/assets/js/plugins/ion-rangeslider/css/ion.rangeSlider.css | |
| stylesheet | //static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css | |
| stylesheet | /library/css/ai-describe.css?r=1&v=1787322557 | |
| js | https://www.google.com/recaptcha/api.js?render=6Lfnb7IZAAAAADX-GXq878JXkc6Qz1RWRTEWvsWs | |
| js | /assets/js/plugins/jquery.min.js | |
| js | /assets/js/plugins/jquery-ui.min.js | |
| js | https://pipe.trueoriginal.com//lang.json?lang=en | |
| js | text/javascript | //static.trueoriginal.com/mini/vvLoading.js?v=1787322557 |
| js | text/javascript | /library/js/freja-eid.js?v=1787322557 |
| js | /site/_codebase/assets/js/plugins/select2/js/select2.full.min.js | |
| js | /site/_codebase/assets/js/plugins/magnific-popup/jquery.magnific-popup.min.js | |
| js | https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js | |
| js | https://js.stripe.com/v3/ | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/core/jquery.slimscroll.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/core/jquery-scrollLock.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/core/jquery.appear.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/core/jquery.countTo.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/core/js.cookie.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/codebase.js?v=1787322557 | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/slick/slick.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/chartjs/Chart.bundle.min.js?v=3.16 | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-notify/bootstrap-notify.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/datatables/jquery.dataTables.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/datatables/dataTables.bootstrap4.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-wizard/jquery.bootstrap.wizard.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/highlightjs/highlight.pack.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-datepicker/js/bootstrap-datepicker.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/flatpickr/flatpickr.js?v=1787322557 | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/flatpickr/plugins/confirmDate/confirmDate.js | |
| js | //static.trueoriginal.com/mini/flatpickr.sv.js?v=1787322557 | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/pwstrength-bootstrap/pwstrength-bootstrap.min.js | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/ion-rangeslider/js/ion.rangeSlider.min.js?v=2.3 | |
| js | //static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-colorpicker/js/bootstrap-colorpicker.min.js | |
| js | //static.trueoriginal.com/mini/true-editor.js?v=1787322557 | |
| js | //static.trueoriginal.com/mini/codeflask.min.js?v=1787322557 | |
| js | https://kit.fontawesome.com/1843dbe3fb.js | |
| js | text/javascript | //static.trueoriginal.com/mini/vvGeneral.js?v=1787322557 |
| js | text/javascript | //static.trueoriginal.com/mini/vvjQuery.js?v=1787322557 |
| js | text/javascript | //static.trueoriginal.com/mini/vvAppend.js?v=1787322557 |
| js | text/javascript | //static.trueoriginal.com/mini/dropzone/dropzone.js?v=1787322557 |
| js | //static.trueoriginal.com/mini/vvPoll.js?v=1787322557 | |
| js | //static.trueoriginal.com/mini/vvUpdate.js?v=1787322557 | |
| js | /mini/vvUpload.js?v=1787322557 | |
| js | /mini/sweetalert.min.js?v=1787322557 | |
| js | text/javascript | //static.trueoriginal.com/mini/ai-describe.js?v=1787322557 |
| js | text/javascript | /mini/true-app.js?v=1787322557 |
| js | module | https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496 |
Серверные заголовки
Кол-во: 14
HTTP-заголовки ответа сервера
?
Заголовки сервера передают браузеру и поисковику служебную информацию: кеширование, безопасность (CSP, HSTS), сжатие (gzip). Правильная настройка ускоряет загрузку и повышает защищённость.
Найдены серверные заголовки 14шт. Подробнее про серверные заголовки.
Показать полный список серверных заголовков
| Ключ | Значение |
|---|---|
| Date | Sun, 23 Aug 2026 10:49:52 GMT |
| Server | cloudflare |
| X-Content-Type-Options | nosniff |
| Cache-Control | no-store, must-revalidate, no-cache |
| Pragma | no-cache |
| X-Frame-Options | SAMEORIGIN |
| Set-Cookie | __true-lang=en; expires=Mon, 23 Nov 2026 11:49:52 GMT; Max-Age=7952400; path=/; domain=.dash.trueoriginal.com; secure; HttpOnly |
| Referrer-Policy | strict-origin-when-cross-origin |
| X-XSS-Protection | 1; mode=block |
| report-to | {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=tNlD2cPbTj3T1UszwFAGy%2BfFZjpFVSAh8LN%2BzjY8fYdlrnPHBPndyAMVxkCDA13dvm9DRn3FnhSvCHnUdFVUQCSqZMiZuuTNDmnbMLvcrpuSJWEZSUiYqPHBm%2FEza6QapxI7rLD4Vw%3D%3D"}]} |
| nel | {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} |
| cf-cache-status | DYNAMIC |
| Server-Timing | cfCacheStatus;desc="DYNAMIC" |
| cf-ray | a2f98d958b83ec5f-DME |
CMS
Система управления сайтом (движок)
?
CMS — это движок, на котором работает сайт (WordPress, 1C-Bitrix, Tilda и др.). Знание CMS помогает понять возможности SEO-оптимизации и подобрать подходящие инструменты. «Не определена» — вероятно, самописный сайт или нестандартная сборка.
Сайт работает на CMS WordPress.
Веб-сервер
Программное обеспечение сервера
?
Веб-сервер — это ПО, которое отдаёт страницы посетителям (nginx, Apache, IIS, LiteSpeed и др.). Определяется по серверным заголовкам ответа (Server, X-Powered-By и т.п.). «Не определён» — сервер намеренно скрывает эти заголовки, это нормальная практика безопасности.
Сайт работает на веб-сервере Cloudflare.
Мета-теги Готовность: 2%
Title
TRUE - Dashboard - Sign in
Заголовок страницы в браузере и поисковой выдаче
?
Title — главный SEO-заголовок страницы. Влияет на CTR в поиске и ранжирование. Оптимальная длина: 50–70 символов. Ключевые слова — ближе к началу.
Необходимо увеличить число символов в title (текущее значение: 26, оптимально: от 40 до 45)
Дублей словоформ в title не найдено.
Description
Описание страницы в поисковой выдаче (сниппет)
?
Meta Description — текст под заголовком в выдаче. Напрямую на позиции не влияет, но влияет на CTR. Оптимальная длина: 120–160 символов.
Установите мета-тег description!
Keywords
Список ключевых слов страницы (устаревший тег)
?
Meta Keywords не учитывается Яндексом и Google для ранжирования с 2009–2012 годов. Заполнение не обязательно, но не вредит. Конкурент может использовать содержимое для анализа.
Установите мета-тег keywords!
Канонический Url
Указывает поисковику основную версию страницы
?
Canonical (rel=canonical) предотвращает проблему дублей страниц. Должен точно совпадать с URL проверяемой страницы. Неправильный canonical может передать ссылочный вес на другую страницу.
Рекомендуем прописать канонический Url.
Robots
Есть критические ошибки
Директивы для поисковых роботов на уровне страницы
?
Meta Robots управляет индексацией конкретной страницы: index/noindex — индексировать ли, follow/nofollow — следовать ли по ссылкам. Noindex полностью исключает страницу из поиска.
Meta-тег robots со значением noindex запрещает индексацию этой страницы. Срочно уберите этот мета-тег!
Адаптивность
width=device-width, initial-scale=1, shrink-to-fit=no
Настройка масштабирования на мобильных устройствах
?
Тег viewport (<meta name="viewport">) сообщает браузеру, как масштабировать страницу на мобильных. Стандарт: width=device-width, initial-scale=1. Отсутствие — признак отсутствия мобильной версии.
Meta-тег viewport со значением-константой width=device-width задаёт ширину страницы в соответствии с размером экрана.
Meta-тег viewport со значением initial-scale=1.0 определяет масштаб 1:1, т.е. «не масштабировать».
Разметка OpenGraph
Не найдено
Мета-теги для красивых превью в соцсетях
?
OpenGraph (og:title, og:description, og:image) управляет тем, как страница выглядит при репосте в социальных сетях и мессенджерах. Отсутствие OG-тегов — невзрачный превью при шеринге.
Разметка OpenGraph не задана. Страница не оптимизирована под социальные сети. Мета-теги с разметкой Og помогают социальным роботам лучше структурировать Ваш сайт.
Все мета-теги
Кол-во: 4
Полный список мета-тегов страницы
?
Таблица всех meta-тегов, включая нестандартные. Позволяет найти опечатки, дубли и лишние теги.
Найдены мета-теги 4шт. Мета-теги не видимы для человека и предназначены для обмена информацией между веб-страницей и поисковыми системами, браузерами и другими веб-службами. С ними роботы 🤖 и устройства ведут себя более ожидаемо.
Показать полный список мета-тегов
| Тип | Название | Значение |
|---|---|---|
| name | viewport | width=device-width, initial-scale=1, shrink-to-fit=no |
| name | description | |
| name | author | |
| name | robots | noindex, nofollow |
Оптимизация Готовность: 75%
Структура
Ошибок нет
Семантические HTML-элементы страницы
?
Проверяет наличие основных структурных элементов: nav, header, footer, main. Корректная семантическая структура помогает поисковику понять архитектуру страницы.
Структура документа корректна (теги <html> и <body> присутствуют по одному на документ).
Контент
Есть ошибки
Объём и качество текстового содержимого
?
Анализирует объём полезного текста на странице. Слишком мало — страница может считаться малополезной. Слишком много — ухудшается читаемость и восприятие.
Абзацев с текстом 6 слишком мало. Добавьте больше абзацев с текстом (тег <p>)!
Кол-во знаков контента 636 слишком мало. Добавьте больше текста!
Кол-во слов 117 слишком мало. Добавьте больше текста (минимум 400 слов)!
Слова из title 4 встречаются в тексте достаточно.
Среднее число слов в абзаце 21 достаточно.
Заголовки
Ошибок нет
Иерархия заголовков H1–H6
?
H1 должен быть один и содержать ключевой запрос. H2–H6 описывают подразделы. Пропуск уровней (H1 → H3) и несколько H1 — типичные ошибки, снижающие понятность страницы для поисковика.
На странице присутствуют заголовки <h1> 5. Это прекрасно.
Тошнота
1,41
Насколько одно слово доминирует в тексте
?
Классическая тошнота = √(частота самого повторяющегося слова). Норма до 7–8: текст воспринимается естественно. Выше — поисковик может счесть страницу переспамленной.
Тошнота страницы в пределах нормы 3.
Академич. тошнота
8,55%
Насколько текст перенасыщен ключевыми словами
?
Академическая тошнота = (частота слова / общее количество слов) × 100%. Показывает долю конкретного слова в тексте. Норма 5–15%.
Академическая тошнота страницы в пределах нормы.
Семантическое ядро
20
Наиболее часто встречающиеся слова на странице
?
Топ слов по частоте использования. Показывает, какие слова доминируют в тексте с точки зрения поисковика.
Контент страницы содержит осмысленный текст и слова.
Показать список слов
| Слово | Кол-во | Частота |
|---|---|---|
| thanks | 2 | 1,71% |
| keeping | 2 | 1,71% |
| password | 2 | 1,71% |
| forgot | 2 | 1,71% |
| password? | 2 | 1,71% |
| welcome | 1 | 0,85% |
| dashboard | 1 | 0,85% |
| remember | 1 | 0,85% |
| loading | 1 | 0,85% |
| generating | 1 | 0,85% |
| fullscreen | 1 | 0,85% |
| remaining | 1 | 0,85% |
| escape | 1 | 0,85% |
| please | 1 | 0,85% |
| warning | 1 | 0,85% |
| progress | 1 | 0,85% |
| unsaved | 1 | 0,85% |
| information | 1 | 0,85% |
| anyway? | 1 | 0,85% |
| cancel | 1 | 0,85% |
Индексация Готовность: 50%
Индексирование
Ошибок нет
Разрешено ли индексирование страницы
?
Проверяет, не закрыта ли страница от индексации через robots.txt, meta robots или X-Robots-Tag. Страница, закрытая от индексации, не появится в поисковой выдаче.
Анкоров на странице 3 оптимально. Поисковые роботы обязательно проиндексируют сайт.
Robots.txt
Найден корректный robots.txt
Файл управления сканированием сайта роботами
?
Robots.txt указывает поисковым роботам, какие страницы сканировать, а какие — нет. Ошибки в файле могут случайно закрыть важные разделы от индексации.
Кол-во редиректов для файла robots.txt 4 слишком большое! Это может вызывать затруднение при индексации поисковыми роботами!
Проверяемая страница не запрещена в robots.txt.
Robots.txt доступен по постоянному адресу
Цепочка редиректов для файла robots.txt:
http://dash.trueoriginal.com/robots.txt
301 MovedPermanently
https://dash.trueoriginal.com/robots.txt
301 MovedPermanently
https://dash.trueoriginal.com/robots.txt/
302 Found
https://dash.trueoriginal.com/sign-in/?r=%2Frobots.txt%2F
200 OK
Показать содержимое robots.txt
<!doctype html>
<!--[if lte IE 9]><html lang="en" class="no-focus lt-ie10 lt-ie10-msg" data-theme="light"><![endif]-->
<!--[if gt IE 9]><!--><html lang="en" class="no-focus" data-theme="light"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>TRUE - Dashboard - Sign in</title>
<meta name="description" content="">
<meta name="author" content="">
<meta name="robots" content="noindex, nofollow">
<script src="https://www.google.com/recaptcha/api.js?render=6Lfnb7IZAAAAADX-GXq878JXkc6Qz1RWRTEWvsWs"></script>
<style>
body {
margin-top: 0.3rem !important;
}
body:before{
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 0.3rem;
background: linear-gradient(90deg, #68D3E6 0%, #ABA2C1 16.14%, #C57BA6 34.52%, #D84B8D 72.23%, #DB2781 100%);
z-index: 2000;
}
</style>
<link href="https://cdnx.truecrt.com/cdn/large/_613a08657d9f9.png?s=2" rel="shortcut icon" type="image/png">
<script type="text/javascript">
(function () {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const cookieName = "__timezone";
const expiryDays = 7; // Refresh every 7 days
// Check if the cookie already exists and matches the current timezone
const existingCookie = document.cookie.split("; ").find(row => row.startsWith(`${cookieName}=`));
if (!existingCookie || existingCookie.split("=")[1] !== timezone) {
// Set or update the cookie
const date = new Date();
date.setTime(date.getTime() + (expiryDays * 24 * 60 * 60 * 1000)); // Expiry in milliseconds
const expires = "expires=" + date.toUTCString();
document.cookie = `${cookieName}=${timezone}; ${expires}; path=/; SameSite=Lax`;
console.log('Set cookie');
}
})();
</script>
<script src="/assets/js/plugins/jquery.min.js"></script>
<script src="/assets/js/plugins/jquery-ui.min.js"></script>
<!-- Plugin Stylesheets -->
<link rel="stylesheet" href="/site/_codebase/assets/js/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="/site/_codebase/assets/js/plugins/datatables/dataTables.bootstrap4.css">
<link rel="stylesheet" href="/site/_codebase/assets/js/plugins/slick/slick.css">
<link rel="stylesheet" href="/site/_codebase/assets/js/plugins/slick/slick-theme.css">
<!-- Fonts and Codebase framework -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.truecdn.io/fonts/true/style.css" id="true-icons">
<link rel="stylesheet" id="css-codebase" href="/site/_codebase/assets/css/codebase.css?v=1787322557">
<link rel="stylesheet" id="css-true" href="/site/_codebase/assets/css/true.css?v=1787322557">
<link rel="stylesheet" id="css-helpers" href="/site/_codebase/assets/css/helpers.css?v=1787322557">
<link rel="stylesheet" id="css-ux" href="/site/_codebase/assets/css/ux.css?v=1787322557">
<link rel="stylesheet" id="css-switch" href="/site/_codebase/assets/css/switch.css?v=1787322557">
<link rel="stylesheet" id="css-ux" href="/site/_codebase/assets/css/dark-mode.css?v=1787322557">
<script async defer src="https://pipe.trueoriginal.com//lang.json?lang=en"></script>
<script type="text/javascript" src="//static.trueoriginal.com/mini/vvLoading.js?v=1787322557"></script>
<script type="text/javascript">
var settings = [];
settings['SERVER'] = 'dash';
settings['VERSION'] = 'live';
settings['LANG'] = 'en';
settings['URL_PATH'] = null;
settings['BASE_PATH'] = "/";
settings['HOST'] = "https://dash.trueoriginal.com/";
settings['TITLE'] = "TRUE - Dashboard";
settings['IMAGE_PATH'] = '';
settings['LOADING_TEXT'] = 'Loading...';
settings['URL_PUSH'] = true;
settings['LOADING'] = 1;
settings['PING_URL'] = "https://ping.trueoriginal.com/";
settings['PIPE_URL'] = "https://pipe.trueoriginal.com/";
settings['PULL_URL'] = "https://pull.trueoriginal.com/";
settings['cdn'] = [];
settings['cdn']['n'] = "https://cdn.truecrt.com/";
settings['cdn']['h'] = "https://cdn-h.truecrt.com/";
settings['cdn']['x'] = "https://cdnx.truecrt.com/";
settings['page.loaded.triggers'] = [];
settings['utoken'] = '';
settings['timeout'] = '';
settings['user_online'] = '';
settings['site_id'] = '';
settings['user_activity_alert'] = false;
settings['google_site_key'] = '6Lfnb7IZAAAAADX-GXq878JXkc6Qz1RWRTEWvsWs';
settings['poll_enabled'] = true;
settings['admin'] = false;
var debugg = "1",
path = "/",
currURL = "https://dash.trueoriginal.com/",
baseUrl = "https://dash.trueoriginal.com/",
template = "site/_codebase/",
domain = "trueoriginal.com",
vvDosubmit = false;
settings['timers'] = [];
settings['uid'] = '';
settings['user_name'] = '';
settings['redis_uid'] = '';
settings['redis_token'] = '';
settings['socket_url'] = '';
var _duration = {};
var IE = false; var myDropZone = null,previewTemplate;
var lang = [];
</script>
</head>
<body id="body" >
<script type="text/javascript">
function getUrlVars(){
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
$(document).ready(function(){
});
if(window.location.pathname != '/sign-in/'){
window.location = '/sign-in/?r='+window.location.pathname;
}
if(window.location.host != 'dash.trueoriginal.com'){
window.location = 'https://dash.trueoriginal.com/sign-in/'+window.location.search;
}
$vars = getUrlVars();
if($vars['r'] == 'closepopup'){
$(document).bind('vvLoginSuccessful',function(){
window.opener.location.reload();
window.close();
});
}
</script>
<script type="text/javascript">
settings['poll_enabled'] = false;
</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Marcellus&display=swap" rel="stylesheet">
<div id="page-container" class="main-content-boxed">
<style type="text/css">
#gradient-canvas {
width:100vh;
height:52vw;
--gradient-color-1: #A5DCE3;
--gradient-color-2: #FFFFFF;
--gradient-color-3: #D72C80;
--gradient-color-4: #FF007D;
z-index:0;
position: absolute;
left: 0px;
top: 0px;
opacity: 1;
transform: rotate(90deg);
transform-origin: 26vw;
}
h1,h2,h3{
font-family: "Marcellus", serif !important;
color: black !important;
}
</style>
<!-- Main Container -->
<main id="main-container" class="esc login-style bg-gradient">
<div class="bg-pattern" id="sketch-holder"></div>
<!-- Page Content -->
<div class="bg-image" style="">
<div class="row mx-0 ">
<div class="hero-static col-md-6 d-none d-md-flex align-items-md-end justify-content-center bg-true-100">
<canvas id="gradient-canvas"></canvas>
<div class="invisible w-100" data-toggle="appear">
<span class="true-icon d-sm-block">
<img src="https://cdn.truecrt.com/cdn/svg/_62e509d1797a9.svg">
</span>
<span class="promo-image mb-20" >
<h1>Thanks for<br>keeping it TRUE.</h1>
</span>
</div>
</div>
<div class="col-md-6 d-flex align-items-center bg-white">
<div class="d-flex align-items-center vh-100 p-50 p-lg-100 w-100 ">
<div class="w-100">
<div class="w-100 mt-auto mb-auto">
<div class="login-card">
<h1 class='text-muted'>Welcome to <b>TRUE Dashboard</b></h1>
<p class="m-b-15 font-regular-large">Enter your email and password to sign in.</p>
<form class="form" idx="user-login-form" action="user-sign-in" method="post" $returnurl="/robots.txt/">
<input name="CSRFtoken" class="user-login-form" type="hidden" value="WFVEVmVVNHdLeUhDOEQ5U3p2Ty82TFF5aHNQQzFQenEzbkNPWVFNdmlKbTRzK0p2K1ZySVI5WW15ZWFtQXlrUzFDMzk1d3QzNDkvVkZSUjljNTlJTkZWbVg0eGplV21iVC9mR1RpbWFKb1k0czZudUFMTjV0M0t5bUNmMDdYQ1dhNEluRWc0YmlJazlJRlhDcHJBUjVqLzBlMTduYzVYQVZhQlU1UVdvdnljPQ==">
<div class="form-group">
<input type="text" name="txtUsername" class="form-control user-login-form" placeholder="Username" value="">
</div>
<div class="form-group">
<input type="password" name="txtPassword" class="form-control user-login-form" placeholder="password" >
</div>
<div class="d-flex justify-content-between align-items-center mb-16">
<div class="">
<div class="custom-control custom-checkbox pl-35">
<input type="checkbox" class="custom-control-input user-login-form" id="login-remember-me" name="remember">
<label class="custom-control-label" for="login-remember-me">Remember Me</label>
</div>
</div>
<div class=" text-right">
<a href="javascript:void(0);" onclick="$('.forgot-card').show();$('.login-card').hide();" class="noAjax">Forgot your password?</a>
</div>
</div>
<button class="btn btn-block btn-true-blue btn-radius btn-lg">Sign in</button>
<div class="text-center pt-10">
</div>
</form>
<div class="freja-eid-divider">
<span>or</span>
</div>
<button type="button" onclick="startFrejaLogin()" class="btn btn-block btn-freja-eid btn-radius btn-lg noAjax" style="border-radius: 0.4rem;">
<img src="https://org.frejaeid.com/wp-content/uploads/2025/09/FrejaIndigo.png" alt="Freja eID" class="freja-eid-logo-img">
Sign in with Freja eID </button>
<div id="motion"></div>
</div>
<!-- Freja eID QR Code Login Area (hidden by default) -->
<div id="freja-login-area" class="text-center" style="display: none;">
<h1 class="text-muted mb-30">Sign in with <b>Freja eID</b></h1>
<p class="mb-10">Scan this QR code with your Freja eID app:</p>
<div id="freja-qr-container" class="freja-qr-container">
<div class="freja-qr-wrapper">
<button type="button" class="freja-qr-button" onclick="openFrejaFullscreen()" aria-label="Freja eID QR code - click for fullscreen">
<img id="freja-qr-image" src="" alt="Freja eID QR code" class="freja-qr-image" style="display: none;" />
<div id="freja-qr-loading" class="freja-loading">
<div class="spinner-border text-dark" role="status">
<span class="sr-only">Loading...</span>
</div>
<p class="mt-10">Generating QR code...</p>
</div>
</button>
<p class="text-muted small mt-10">Click on QR code for fullscreen mode</p>
</div>
</div>
<div id="freja-status-text" class="text-center text-muted mt-5"></div>
<div id="freja-timer" class="freja-timer mt-15" style="display: none;">
<div class="freja-timer-display">
<i class="fa fa-clock-o"></i>
<span id="freja-countdown">2:00</span> remaining </div>
</div>
<div id="freja-error" class="text-center mt-15" style="display: none;">
<div class="alert alert-danger" role="alert">
<span id="freja-error-message"></span>
</div>
<button type="button" onclick="retryFrejaAuth()" class="btn btn-true-blue btn-radius btn-lg btn-block mb-10">
Try again </button>
</div>
<div class="text-center pt-15">
<a href="javascript:void(0);" onclick="cancelFrejaAuth()" class="noAjax text-gray text-link text-opacity">
<i class="fa fa-arrow-left"></i> Back to login </a>
</div>
</div>
<!-- Freja eID QR Fullscreen Overlay -->
<div id="freja-fullscreen" class="freja-fullscreen" style="display: none;">
<div class="freja-fullscreen-content">
<button class="freja-fullscreen-close" onclick="closeFrejaFullscreen()" aria-label="Close fullscreen">
<i class="fa fa-times"></i>
</button>
<div class="freja-fullscreen-qr">
<img id="freja-fullscreen-image" src="" alt="Freja eID QR code fullscreen" />
</div>
<p class="freja-fullscreen-instructions">
Scan the QR code with your Freja eID app. Press Escape or click to close. </p>
<div id="freja-fullscreen-timer" class="freja-fullscreen-timer"></div>
</div>
</div>
<div class="forgot-card " style="display: none;">
<h1 class="" style="margin-bottom: 30px;">Forgot your password?</h1>
<p class="m-b-15 font-regular-large">Please enter your email to get your password</p>
<form class="form" idx="user-forgot-form" action="user-password-reset">
<input name="CSRFtoken" class="user-forgot-form" type="hidden" value="WFVEVmVVNHdLeUhDOEQ5U3p2Ty82TFF5aHNQQzFQenEzbkNPWVFNdmlKbTRzK0p2K1ZySVI5WW15ZWFtQXlrUzFDMzk1d3QzNDkvVkZSUjljNTlJTkZWbVg0eGplV21iVC9mR1RpbWFKb1k0czZudUFMTjV0M0t5bUNmMDdYQ1dhNEluRWc0YmlJazlJRlhDcHJBUjVqLzBlMTduYzVYQVZhQlU1UVdvdnljPQ==">
<div class="form-group">
<input type="email" name="txtUsername" class="form-control user-forgot-form" placeholder="Username or e-mail">
</div>
<button class="btn btn-block btn-true-blue btn-radius btn-lg">Send</button>
<div class="text-center pt-10">
<a href="javascript:void(0);" onclick="$('.forgot-card').hide();$('.login-card').show();" class="noAjax text-gray text-link text-opacity"><i class="fa fa-arrow-left"></i> Go back</a>
</div>
</form>
</div>
</div>
<span class="true-icon d-md-none">
<img src="https://cdn.truecrt.com/cdn/svg/_62e509d1797a9.svg">
</span>
<h1 class="mt-15 d-md-none" style="position: absolute;bottom: 20px;font-size: 1.3rem;line-height: 1.8rem;">Thanks for<br>keeping it TRUE.</h1>
</div>
</div>
</div>
</div>
</div>
<!-- END Page Content -->
</main>
<!-- END Main Container -->
</div>
<!-- END Page Container -->
<style type="text/css">
.promo-image {
display: inline-block;
width: 80%;
margin-left: 6%;
;
}
.promo-image img {
width: 100%;
max-width:684px;
}
.true-icon {
position: absolute;
top: 67px;
left: 8%;
}
.true-icon img{
width: 45px;
}
#page-footer {
display: none !important;
}
#page-container {
position: relative;
z-index: 1;
}
/* Freja eID Button Styles */
.freja-eid-divider {
text-align: center;
margin: 20px 0;
position: relative;
}
.freja-eid-divider::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: #e0e0e0;
}
.freja-eid-divider span {
background: #fff;
padding: 0 15px;
position: relative;
color: #999;
font-size: 13px;
text-transform: uppercase;
}
.btn-freja-eid {
background-color: #0A2540;
color: #fff !important;
border: none;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
font-weight: 500;
transition: background-color 0.2s ease;
}
.btn-freja-eid:hover {
background-color: #163a5c;
color: #fff !important;
}
.btn-freja-eid .freja-eid-logo-img {
height: 12px;
width: auto;
filter: brightness(0) invert(1);
}
/* Freja eID QR Code Area */
.freja-qr-container {
text-align: center;
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
padding: 10px 0;
}
.freja-qr-wrapper {
text-align: center;
}
.freja-qr-button {
background: none;
border: 2px solid #e8e8e8;
border-radius: 12px;
padding: 15px;
cursor: pointer;
transition: border-color 0.2s, box-shadow 0.2s;
display: inline-block;
}
.freja-qr-button:hover {
border-color: #ccc;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
}
.freja-qr-image {
max-width: 220px;
width: 100%;
height: auto;
display: block;
}
.freja-loading {
padding: 40px 60px;
text-align: center;
}
.freja-success {
padding: 30px 0;
text-align: center;
}
.freja-success i {
font-size: 48px;
}
.freja-timer-display {
font-weight: 600;
font-size: 15px;
color: #333;
}
.freja-timer-display i {
margin-right: 5px;
}
/* Freja Fullscreen QR */
.freja-fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.85);
z-index: 99999;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.freja-fullscreen-content {
text-align: center;
position: relative;
cursor: default;
}
.freja-fullscreen-close {
position: absolute;
top: -40px;
right: -40px;
background: none;
border: none;
color: #fff;
font-size: 28px;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.2s;
}
.freja-fullscreen-close:hover {
opacity: 1;
}
.freja-fullscreen-qr img {
max-width: 400px;
width: 90vw;
height: auto;
border-radius: 12px;
background: #fff;
padding: 20px;
}
.freja-fullscreen-instructions {
color: rgba(255,255,255,0.7);
margin-top: 20px;
font-size: 14px;
}
.freja-fullscreen-timer {
color: #fff;
font-weight: 600;
margin-top: 10px;
font-size: 16px;
}
</style>
<script type="text/javascript">
//Converting colors to proper format
function normalizeColor(hexCode) {
return [(hexCode >> 16 & 255) / 255, (hexCode >> 8 & 255) / 255, (255 & hexCode) / 255]
} ["SCREEN", "LINEAR_LIGHT"].reduce((hexCode, t, n) => Object.assign(hexCode, {
[t]: n
}), {});
//Essential functionality of WebGl
//t = width
//n = height
class MiniGl {
constructor(canvas, width, height, debug = false) {
const _miniGl = this,
debug_output = -1 !== document.location.search.toLowerCase().indexOf("debug=webgl");
_miniGl.canvas = canvas, _miniGl.gl = _miniGl.canvas.getContext("webgl", {
antialias: true
}), _miniGl.meshes = [];
const context = _miniGl.gl;
width && height && this.setSize(width, height), _miniGl.lastDebugMsg, _miniGl.debug = debug && debug_output ? function(e) {
const t = new Date;
t - _miniGl.lastDebugMsg > 1e3 && console.log("---"), console.log(t.toLocaleTimeString() + Array(Math.max(0, 32 - e.length)).join(" ") + e + ": ", ...Array.from(arguments).slice(1)), _miniGl.lastDebugMsg = t
} : () => {}, Object.defineProperties(_miniGl, {
Material: {
enumerable: false,
value: class {
constructor(vertexShaders, fragments, uniforms = {}) {
const material = this;
function getShaderByType(type, source) {
const shader = context.createShader(type);
return context.shaderSource(shader, source), context.compileShader(shader), context.getShaderParameter(shader, context.COMPILE_STATUS) || console.error(context.getShaderInfoLog(shader)), _miniGl.debug("Material.compileShaderSource", {
source: source
}), shader
}
function getUniformVariableDeclarations(uniforms, type) {
return Object.entries(uniforms).map(([uniform, value]) => value.getDeclaration(uniform, type)).join("\n")
}
material.uniforms = uniforms, material.uniformInstances = [];
const prefix = "\n precision highp float;\n ";
material.vertexSource = `\n ${prefix}\n attribute vec4 position;\n attribute vec2 uv;\n attribute vec2 uvNorm;\n ${getUniformVariableDeclarations(_miniGl.commonUniforms,"vertex")}\n ${getUniformVariableDeclarations(uniforms,"vertex")}\n ${vertexShaders}\n `,
material.Source = `\n ${prefix}\n ${getUniformVariableDeclarations(_miniGl.commonUniforms,"fragment")}\n ${getUniformVariableDeclarations(uniforms,"fragment")}\n ${fragments}\n `,
material.vertexShader = getShaderByType(context.VERTEX_SHADER, material.vertexSource),
material.fragmentShader = getShaderByType(context.FRAGMENT_SHADER, material.Source),
material.program = context.createProgram(),
context.attachShader(material.program, material.vertexShader),
context.attachShader(material.program, material.fragmentShader),
context.linkProgram(material.program),
context.getProgramParameter(material.program, context.LINK_STATUS) || console.error(context.getProgramInfoLog(material.program)),
context.useProgram(material.program),
material.attachUniforms(void 0, _miniGl.commonUniforms),
material.attachUniforms(void 0, material.uniforms)
}
//t = uniform
attachUniforms(name, uniforms) {
//n = material
const material = this;
void 0 === name ? Object.entries(uniforms).forEach(([name, uniform]) => {
material.attachUniforms(name, uniform)
}) : "array" == uniforms.type ? uniforms.value.forEach((uniform, i) => material.attachUniforms(`${name}[${i}]`, uniform)) : "struct" == uniforms.type ? Object.entries(uniforms.value).forEach(([uniform, i]) => material.attachUniforms(`${name}.${uniform}`, i)) : (_miniGl.debug("Material.attachUniforms", {
name: name,
uniform: uniforms
}), material.uniformInstances.push({
uniform: uniforms,
location: context.getUniformLocation(material.program, name)
}))
}
}
},
Uniform: {
enumerable: !1,
value: class {
constructor(e) {
this.type = "float", Object.assign(this, e);
this.typeFn = {
float: "1f",
int: "1i",
vec2: "2fv",
vec3: "3fv",
vec4: "4fv",
mat4: "Matrix4fv"
} [this.type] || "1f", this.update()
}
update(value) {
void 0 !== this.value && context[`uniform${this.typeFn}`](value, 0 === this.typeFn.indexOf("Matrix") ? this.transpose : this.value, 0 === this.typeFn.indexOf("Matrix") ? this.value : null)
}
//e - name
//t - type
//n - length
getDeclaration(name, type, length) {
const uniform = this;
if (uniform.excludeFrom !== type) {
if ("array" === uniform.type) return uniform.value[0].getDeclaration(name, type, uniform.value.length) + `\nconst int ${name}_length = ${uniform.value.length};`;
if ("struct" === uniform.type) {
let name_no_prefix = name.replace("u_", "");
return name_no_prefix =
name_no_prefix.charAt(0).toUpperCase() +
name_no_prefix.slice(1),
`uniform struct ${name_no_prefix}
{\n` +
Object.entries(uniform.value).map(([name, uniform]) =>
uniform.getDeclaration(name, type)
.replace(/^uniform/, ""))
.join("")
+ `\n} ${name}${length>0?`[${length}]`:""};`
}
return `uniform ${uniform.type} ${name}${length>0?`[${length}]`:""};`
}
}
}
},
PlaneGeometry: {
enumerable: !1,
value: class {
constructor(width, height, n, i, orientation) {
context.createBuffer(), this.attributes = {
position: new _miniGl.Attribute({
target: context.ARRAY_BUFFER,
size: 3
}),
uv: new _miniGl.Attribute({
target: context.ARRAY_BUFFER,
size: 2
}),
uvNorm: new _miniGl.Attribute({
target: context.ARRAY_BUFFER,
size: 2
}),
index: new _miniGl.Attribute({
target: context.ELEMENT_ARRAY_BUFFER,
size: 3,
type: context.UNSIGNED_SHORT
})
}, this.setTopology(n, i), this.setSize(width, height, orientation)
}
setTopology(e = 1, t = 1) {
const n = this;
n.xSegCount = e, n.ySegCount = t, n.vertexCount = (n.xSegCount + 1) * (n.ySegCount + 1), n.quadCount = n.xSegCount * n.ySegCount * 2, n.attributes.uv.values = new Float32Array(2 * n.vertexCount), n.attributes.uvNorm.values = new Float32Array(2 * n.vertexCount), n.attributes.index.values = new Uint16Array(3 * n.quadCount);
for (let e = 0; e <= n.ySegCount; e++)
for (let t = 0; t <= n.xSegCount; t++) {
const i = e * (n.xSegCount + 1) + t;
if (n.attributes.uv.values[2 * i] = t / n.xSegCount, n.attributes.uv.values[2 * i + 1] = 1 - e / n.ySegCount, n.attributes.uvNorm.values[2 * i] = t / n.xSegCount * 2 - 1, n.attributes.uvNorm.values[2 * i + 1] = 1 - e / n.ySegCount * 2, t < n.xSegCount && e < n.ySegCount) {
const s = e * n.xSegCount + t;
n.attributes.index.values[6 * s] = i, n.attributes.index.values[6 * s + 1] = i + 1 + n.xSegCount, n.attributes.index.values[6 * s + 2] = i + 1, n.attributes.index.values[6 * s + 3] = i + 1, n.attributes.index.values[6 * s + 4] = i + 1 + n.xSegCount, n.attributes.index.values[6 * s + 5] = i + 2 + n.xSegCount
}
}
n.attributes.uv.update(), n.attributes.uvNorm.update(), n.attributes.index.update(), _miniGl.debug("Geometry.setTopology", {
uv: n.attributes.uv,
uvNorm: n.attributes.uvNorm,
index: n.attributes.index
})
}
setSize(width = 1, height = 1, orientation = "xz") {
const geometry = this;
geometry.width = width,
geometry.height = height,
geometry.orientation = orientation,
geometry.attributes.position.values && geometry.attributes.position.values.length === 3 * geometry.vertexCount
|| (geometry.attributes.position.values = new Float32Array(3 * geometry.vertexCount));
const o = width / -2,
r = height / -2,
segment_width = width / geometry.xSegCount,
segment_height = height / geometry.ySegCount;
for (let yIndex= 0; yIndex <= geometry.ySegCount; yIndex++) {
const t = r + yIndex * segment_height;
for (let xIndex = 0; xIndex <= geometry.xSegCount; xIndex++) {
const r = o + xIndex * segment_width,
l = yIndex * (geometry.xSegCount + 1) + xIndex;
geometry.attributes.position.values[3 * l + "xyz".indexOf(orientation[0])] = r,
geometry.attributes.position.values[3 * l + "xyz".indexOf(orientation[1])] = -t
}
}
geometry.attributes.position.update(), _miniGl.debug("Geometry.setSize", {
position: geometry.attributes.position
})
}
}
},
Mesh: {
enumerable: !1,
value: class {
constructor(geometry, material) {
const mesh = this;
mesh.geometry = geometry, mesh.material = material, mesh.wireframe = !1, mesh.attributeInstances = [], Object.entries(mesh.geometry.attributes).forEach(([e, attribute]) => {
mesh.attributeInstances.push({
attribute: attribute,
location: attribute.attach(e, mesh.material.program)
})
}), _miniGl.meshes.push(mesh), _miniGl.debug("Mesh.constructor", {
mesh: mesh
})
}
draw() {
context.useProgram(this.material.program), this.material.uniformInstances.forEach(({
uniform: e,
location: t
}) => e.update(t)), this.attributeInstances.forEach(({
attribute: e,
location: t
}) => e.use(t)), context.drawElements(this.wireframe ? context.LINES : context.TRIANGLES, this.geometry.attributes.index.values.length, context.UNSIGNED_SHORT, 0)
}
remove() {
_miniGl.meshes = _miniGl.meshes.filter(e => e != this)
}
}
},
Attribute: {
enumerable: !1,
value: class {
constructor(e) {
this.type = context.FLOAT, this.normalized = !1, this.buffer = context.createBuffer(), Object.assign(this, e), this.update()
}
update() {
void 0 !== this.values && (context.bindBuffer(this.target, this.buffer), context.bufferData(this.target, this.values, context.STATIC_DRAW))
}
attach(e, t) {
const n = context.getAttribLocation(t, e);
return this.target === context.ARRAY_BUFFER && (context.enableVertexAttribArray(n), context.vertexAttribPointer(n, this.size, this.type, this.normalized, 0, 0)), n
}
use(e) {
context.bindBuffer(this.target, this.buffer), this.target === context.ARRAY_BUFFER && (context.enableVertexAttribArray(e), context.vertexAttribPointer(e, this.size, this.type, this.normalized, 0, 0))
}
}
}
});
const a = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
_miniGl.commonUniforms = {
projectionMatrix: new _miniGl.Uniform({
type: "mat4",
value: a
}),
modelViewMatrix: new _miniGl.Uniform({
type: "mat4",
value: a
}),
resolution: new _miniGl.Uniform({
type: "vec2",
value: [1, 1]
}),
aspectRatio: new _miniGl.Uniform({
type: "float",
value: 1
})
}
}
setSize(e = 640, t = 480) {
this.width = e, this.height = t, this.canvas.width = e, this.canvas.height = t, this.gl.viewport(0, 0, e, t), this.commonUniforms.resolution.value = [e, t], this.commonUniforms.aspectRatio.value = e / t, this.debug("MiniGL.setSize", {
width: e,
height: t
})
}
//left, right, top, bottom, near, far
setOrthographicCamera(e = 0, t = 0, n = 0, i = -2e3, s = 2e3) {
this.commonUniforms.projectionMatrix.value = [2 / this.width, 0, 0, 0, 0, 2 / this.height, 0, 0, 0, 0, 2 / (i - s), 0, e, t, n, 1], this.debug("setOrthographicCamera", this.commonUniforms.projectionMatrix.value)
}
render() {
this.gl.clearColor(0, 0, 0, 0), this.gl.clearDepth(1), this.meshes.forEach(e => e.draw())
}
}
//Sets initial properties
function e(object, propertyName, val) {
return propertyName in object ? Object.defineProperty(object, propertyName, {
value: val,
enumerable: !0,
configurable: !0,
writable: !0
}) : object[propertyName] = val, object
}
//Gradient object
class Gradient {
constructor(...t) {
e(this, "el", void 0), e(this, "cssVarRetries", 0), e(this, "maxCssVarRetries", 200), e(this, "angle", 0), e(this, "isLoadedClass", !1), e(this, "isScrolling", !1), /*e(this, "isStatic", o.disableAmbientAnimations()),*/ e(this, "scrollingTimeout", void 0), e(this, "scrollingRefreshDelay", 200), e(this, "isIntersecting", !1), e(this, "shaderFiles", void 0), e(this, "vertexShader", void 0), e(this, "sectionColors", void 0), e(this, "computedCanvasStyle", void 0), e(this, "conf", void 0), e(this, "uniforms", void 0), e(this, "t", 1253106), e(this, "last", 0), e(this, "width", void 0), e(this, "minWidth", 1111), e(this, "height", 600), e(this, "xSegCount", void 0), e(this, "ySegCount", void 0), e(this, "mesh", void 0), e(this, "material", void 0), e(this, "geometry", void 0), e(this, "minigl", void 0), e(this, "scrollObserver", void 0), e(this, "amp", 320), e(this, "seed", 5), e(this, "freqX", 14e-5), e(this, "freqY", 29e-5), e(this, "freqDelta", 1e-5), e(this, "activeColors", [1, 1, 1, 1]), e(this, "isMetaKey", !1), e(this, "isGradientLegendVisible", !1), e(this, "isMouseDown", !1), e(this, "handleScroll", () => {
clearTimeout(this.scrollingTimeout), this.scrollingTimeout = setTimeout(this.handleScrollEnd, this.scrollingRefreshDelay), this.isGradientLegendVisible && this.hideGradientLegend(), this.conf.playing && (this.isScrolling = !0, this.pause())
}), e(this, "handleScrollEnd", () => {
this.isScrolling = !1, this.isIntersecting && this.play()
}), e(this, "resize", () => {
this.width = window.innerWidth, this.minigl.setSize(this.width, this.height), this.minigl.setOrthographicCamera(), this.xSegCount = Math.ceil(this.width * this.conf.density[0]), this.ySegCount = Math.ceil(this.height * this.conf.density[1]), this.mesh.geometry.setTopology(this.xSegCount, this.ySegCount), this.mesh.geometry.setSize(this.width, this.height), this.mesh.material.uniforms.u_shadow_power.value = this.width < 600 ? 5 : 6
}), e(this, "handleMouseDown", e => {
this.isGradientLegendVisible && (this.isMetaKey = e.metaKey, this.isMouseDown = !0, !1 === this.conf.playing && requestAnimationFrame(this.animate))
}), e(this, "handleMouseUp", () => {
this.isMouseDown = !1
}), e(this, "animate", e => {
if (!this.shouldSkipFrame(e) || this.isMouseDown) {
if (this.t += Math.min(e - this.last, 1e3 / 15), this.last = e, this.isMouseDown) {
let e = 160;
this.isMetaKey && (e = -160), this.t += e
}
this.mesh.material.uniforms.u_time.value = this.t, this.minigl.render()
}
if (0 !== this.last && this.isStatic) return this.minigl.render(), void this.disconnect();
(/*this.isIntersecting && */this.conf.playing || this.isMouseDown) && requestAnimationFrame(this.animate)
}), e(this, "addIsLoadedClass", () => {
/*this.isIntersecting && */!this.isLoadedClass && (this.isLoadedClass = !0, this.el.classList.add("isLoaded"), setTimeout(() => {
this.el.parentElement.classList.add("isLoaded")
}, 3e3))
}), e(this, "pause", () => {
this.conf.playing = false
}), e(this, "play", () => {
requestAnimationFrame(this.animate), this.conf.playing = true
}), e(this,"initGradient", (selector) => {
this.el = document.querySelector(selector);
this.connect();
return this;
})
}
async connect() {
this.shaderFiles = {
vertex: "varying vec3 v_color;\n\nvoid main() {\n float time = u_time * u_global.noiseSpeed;\n\n vec2 noiseCoord = resolution * uvNorm * u_global.noiseFreq;\n\n vec2 st = 1. - uvNorm.xy;\n\n //\n // Tilting the plane\n //\n\n // Front-to-back tilt\n float tilt = resolution.y / 2.0 * uvNorm.y;\n\n // Left-to-right angle\n float incline = resolution.x * uvNorm.x / 2.0 * u_vertDeform.incline;\n\n // Up-down shift to offset incline\n float offset = resolution.x / 2.0 * u_vertDeform.incline * mix(u_vertDeform.offsetBottom, u_vertDeform.offsetTop, uv.y);\n\n //\n // Vertex noise\n //\n\n float noise = snoise(vec3(\n noiseCoord.x * u_vertDeform.noiseFreq.x + time * u_vertDeform.noiseFlow,\n noiseCoord.y * u_vertDeform.noiseFreq.y,\n time * u_vertDeform.noiseSpeed + u_vertDeform.noiseSeed\n )) * u_vertDeform.noiseAmp;\n\n // Fade noise to zero at edges\n noise *= 1.0 - pow(abs(uvNorm.y), 2.0);\n\n // Clamp to 0\n noise = max(0.0, noise);\n\n vec3 pos = vec3(\n position.x,\n position.y + tilt + incline + noise - offset,\n position.z\n );\n\n //\n // Vertex color, to be passed to fragment shader\n //\n\n if (u_active_colors[0] == 1.) {\n v_color = u_baseColor;\n }\n\n for (int i = 0; i < u_waveLayers_length; i++) {\n if (u_active_colors[i + 1] == 1.) {\n WaveLayers layer = u_waveLayers[i];\n\n float noise = smoothstep(\n layer.noiseFloor,\n layer.noiseCeil,\n snoise(vec3(\n noiseCoord.x * layer.noiseFreq.x + time * layer.noiseFlow,\n noiseCoord.y * layer.noiseFreq.y,\n time * layer.noiseSpeed + layer.noiseSeed\n )) / 2.0 + 0.5\n );\n\n v_color = blendNormal(v_color, layer.color, pow(noise, 4.));\n }\n }\n\n //\n // Finish\n //\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);\n}",
noise: "//\n// Description : Array and textureless GLSL 2D/3D/4D simplex\n// noise functions.\n// Author : Ian McEwan, Ashima Arts.\n// Maintainer : stegu\n// Lastmod : 20110822 (ijm)\n// License : Copyright (C) 2011 Ashima Arts. All rights reserved.\n// Distributed under the MIT License. See LICENSE file.\n// https://github.com/ashima/webgl-noise\n// https://github.com/stegu/webgl-noise\n//\n\nvec3 mod289(vec3 x) {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nvec4 mod289(vec4 x) {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nvec4 permute(vec4 x) {\n return mod289(((x*34.0)+1.0)*x);\n}\n\nvec4 taylorInvSqrt(vec4 r)\n{\n return 1.79284291400159 - 0.85373472095314 * r;\n}\n\nfloat snoise(vec3 v)\n{\n const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\n// First corner\n vec3 i = floor(v + dot(v, C.yyy) );\n vec3 x0 = v - i + dot(i, C.xxx) ;\n\n// Other corners\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min( g.xyz, l.zxy );\n vec3 i2 = max( g.xyz, l.zxy );\n\n // x0 = x0 - 0.0 + 0.0 * C.xxx;\n // x1 = x0 - i1 + 1.0 * C.xxx;\n // x2 = x0 - i2 + 2.0 * C.xxx;\n // x3 = x0 - 1.0 + 3.0 * C.xxx;\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y\n vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y\n\n// Permutations\n i = mod289(i);\n vec4 p = permute( permute( permute(\n i.z + vec4(0.0, i1.z, i2.z, 1.0 ))\n + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))\n + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));\n\n// Gradients: 7x7 points over a square, mapped onto an octahedron.\n// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)\n float n_ = 0.142857142857; // 1.0/7.0\n vec3 ns = n_ * D.wyz - D.xzx;\n\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)\n\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\n vec4 x = x_ *ns.x + ns.yyyy;\n vec4 y = y_ *ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n\n vec4 b0 = vec4( x.xy, y.xy );\n vec4 b1 = vec4( x.zw, y.zw );\n\n //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;\n //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;\n vec4 s0 = floor(b0)*2.0 + 1.0;\n vec4 s1 = floor(b1)*2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n\n vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;\n vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;\n\n vec3 p0 = vec3(a0.xy,h.x);\n vec3 p1 = vec3(a0.zw,h.y);\n vec3 p2 = vec3(a1.xy,h.z);\n vec3 p3 = vec3(a1.zw,h.w);\n\n//Normalise gradients\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n p0 *= norm.x;\n p1 *= norm.y;\n p2 *= norm.z;\n p3 *= norm.w;\n\n// Mix final noise value\n vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n m = m * m;\n return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),\n dot(p2,x2), dot(p3,x3) ) );\n}",
blend: "//\n// https://github.com/jamieowen/glsl-blend\n//\n\n// Normal\n\nvec3 blendNormal(vec3 base, vec3 blend) {\n\treturn blend;\n}\n\nvec3 blendNormal(vec3 base, vec3 blend, float opacity) {\n\treturn (blendNormal(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Screen\n\nfloat blendScreen(float base, float blend) {\n\treturn 1.0-((1.0-base)*(1.0-blend));\n}\n\nvec3 blendScreen(vec3 base, vec3 blend) {\n\treturn vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));\n}\n\nvec3 blendScreen(vec3 base, vec3 blend, float opacity) {\n\treturn (blendScreen(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Multiply\n\nvec3 blendMultiply(vec3 base, vec3 blend) {\n\treturn base*blend;\n}\n\nvec3 blendMultiply(vec3 base, vec3 blend, float opacity) {\n\treturn (blendMultiply(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Overlay\n\nfloat blendOverlay(float base, float blend) {\n\treturn base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));\n}\n\nvec3 blendOverlay(vec3 base, vec3 blend) {\n\treturn vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b));\n}\n\nvec3 blendOverlay(vec3 base, vec3 blend, float opacity) {\n\treturn (blendOverlay(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Hard light\n\nvec3 blendHardLight(vec3 base, vec3 blend) {\n\treturn blendOverlay(blend,base);\n}\n\nvec3 blendHardLight(vec3 base, vec3 blend, float opacity) {\n\treturn (blendHardLight(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Soft light\n\nfloat blendSoftLight(float base, float blend) {\n\treturn (blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend));\n}\n\nvec3 blendSoftLight(vec3 base, vec3 blend) {\n\treturn vec3(blendSoftLight(base.r,blend.r),blendSoftLight(base.g,blend.g),blendSoftLight(base.b,blend.b));\n}\n\nvec3 blendSoftLight(vec3 base, vec3 blend, float opacity) {\n\treturn (blendSoftLight(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Color dodge\n\nfloat blendColorDodge(float base, float blend) {\n\treturn (blend==1.0)?blend:min(base/(1.0-blend),1.0);\n}\n\nvec3 blendColorDodge(vec3 base, vec3 blend) {\n\treturn vec3(blendColorDodge(base.r,blend.r),blendColorDodge(base.g,blend.g),blendColorDodge(base.b,blend.b));\n}\n\nvec3 blendColorDodge(vec3 base, vec3 blend, float opacity) {\n\treturn (blendColorDodge(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Color burn\n\nfloat blendColorBurn(float base, float blend) {\n\treturn (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0);\n}\n\nvec3 blendColorBurn(vec3 base, vec3 blend) {\n\treturn vec3(blendColorBurn(base.r,blend.r),blendColorBurn(base.g,blend.g),blendColorBurn(base.b,blend.b));\n}\n\nvec3 blendColorBurn(vec3 base, vec3 blend, float opacity) {\n\treturn (blendColorBurn(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Vivid Light\n\nfloat blendVividLight(float base, float blend) {\n\treturn (blend<0.5)?blendColorBurn(base,(2.0*blend)):blendColorDodge(base,(2.0*(blend-0.5)));\n}\n\nvec3 blendVividLight(vec3 base, vec3 blend) {\n\treturn vec3(blendVividLight(base.r,blend.r),blendVividLight(base.g,blend.g),blendVividLight(base.b,blend.b));\n}\n\nvec3 blendVividLight(vec3 base, vec3 blend, float opacity) {\n\treturn (blendVividLight(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Lighten\n\nfloat blendLighten(float base, float blend) {\n\treturn max(blend,base);\n}\n\nvec3 blendLighten(vec3 base, vec3 blend) {\n\treturn vec3(blendLighten(base.r,blend.r),blendLighten(base.g,blend.g),blendLighten(base.b,blend.b));\n}\n\nvec3 blendLighten(vec3 base, vec3 blend, float opacity) {\n\treturn (blendLighten(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Linear burn\n\nfloat blendLinearBurn(float base, float blend) {\n\t// Note : Same implementation as BlendSubtractf\n\treturn max(base+blend-1.0,0.0);\n}\n\nvec3 blendLinearBurn(vec3 base, vec3 blend) {\n\t// Note : Same implementation as BlendSubtract\n\treturn max(base+blend-vec3(1.0),vec3(0.0));\n}\n\nvec3 blendLinearBurn(vec3 base, vec3 blend, float opacity) {\n\treturn (blendLinearBurn(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Linear dodge\n\nfloat blendLinearDodge(float base, float blend) {\n\t// Note : Same implementation as BlendAddf\n\treturn min(base+blend,1.0);\n}\n\nvec3 blendLinearDodge(vec3 base, vec3 blend) {\n\t// Note : Same implementation as BlendAdd\n\treturn min(base+blend,vec3(1.0));\n}\n\nvec3 blendLinearDodge(vec3 base, vec3 blend, float opacity) {\n\treturn (blendLinearDodge(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Linear light\n\nfloat blendLinearLight(float base, float blend) {\n\treturn blend<0.5?blendLinearBurn(base,(2.0*blend)):blendLinearDodge(base,(2.0*(blend-0.5)));\n}\n\nvec3 blendLinearLight(vec3 base, vec3 blend) {\n\treturn vec3(blendLinearLight(base.r,blend.r),blendLinearLight(base.g,blend.g),blendLinearLight(base.b,blend.b));\n}\n\nvec3 blendLinearLight(vec3 base, vec3 blend, float opacity) {\n\treturn (blendLinearLight(base, blend) * opacity + base * (1.0 - opacity));\n}",
fragment: "varying vec3 v_color;\n\nvoid main() {\n vec3 color = v_color;\n if (u_darken_top == 1.0) {\n vec2 st = gl_FragCoord.xy/resolution.xy;\n color.g -= pow(st.y + sin(-12.0) * st.x, u_shadow_power) * 0.4;\n }\n gl_FragColor = vec4(color, 1.0);\n}"
},
this.conf = {
presetName: "",
wireframe: false,
density: [.06, .16],
zoom: 1,
rotation: 0,
playing: true
},
document.querySelectorAll("canvas").length < 1 ? console.log("DID NOT LOAD HERO STRIPE CANVAS") : (
this.minigl = new MiniGl(this.el, null, null, !0),
requestAnimationFrame(() => {
this.el && (this.computedCanvasStyle = getComputedStyle(this.el), this.waitForCssVars())
})
/*
this.scrollObserver = await s.create(.1, !1),
this.scrollObserver.observe(this.el),
this.scrollObserver.onSeparate(() => {
window.removeEventListener("scroll", this.handleScroll), window.removeEventListener("mousedown", this.handleMouseDown), window.removeEventListener("mouseup", this.handleMouseUp), window.removeEventListener("keydown", this.handleKeyDown), this.isIntersecting = !1, this.conf.playing && this.pause()
}),
this.scrollObserver.onIntersect(() => {
window.addEventListener("scroll", this.handleScroll), window.addEventListener("mousedown", this.handleMouseDown), window.addEventListener("mouseup", this.handleMouseUp), window.addEventListener("keydown", this.handleKeyDown), this.isIntersecting = !0, this.addIsLoadedClass(), this.play()
})*/
)
}
disconnect() {
this.scrollObserver && (window.removeEventListener("scroll", this.handleScroll), window.removeEventListener("mousedown", this.handleMouseDown), window.removeEventListener("mouseup", this.handleMouseUp), window.removeEventListener("keydown", this.handleKeyDown), this.scrollObserver.disconnect()), window.removeEventListener("resize", this.resize)
}
initMaterial() {
this.uniforms = {
u_time: new this.minigl.Uniform({
value: 0
}),
u_shadow_power: new this.minigl.Uniform({
value: 5
}),
u_darken_top: new this.minigl.Uniform({
value: "" === this.el.dataset.jsDarkenTop ? 1 : 0
}),
u_active_colors: new this.minigl.Uniform({
value: this.activeColors,
type: "vec4"
}),
u_global: new this.minigl.Uniform({
value: {
noiseFreq: new this.minigl.Uniform({
value: [this.freqX, this.freqY],
type: "vec2"
}),
noiseSpeed: new this.minigl.Uniform({
value: 5e-6
})
},
type: "struct"
}),
u_vertDeform: new this.minigl.Uniform({
value: {
incline: new this.minigl.Uniform({
value: Math.sin(this.angle) / Math.cos(this.angle)
}),
offsetTop: new this.minigl.Uniform({
value: -.5
}),
offsetBottom: new this.minigl.Uniform({
value: -.5
}),
noiseFreq: new this.minigl.Uniform({
value: [3, 4],
type: "vec2"
}),
noiseAmp: new this.minigl.Uniform({
value: this.amp
}),
noiseSpeed: new this.minigl.Uniform({
value: 10
}),
noiseFlow: new this.minigl.Uniform({
value: 3
}),
noiseSeed: new this.minigl.Uniform({
value: this.seed
})
},
type: "struct",
excludeFrom: "fragment"
}),
u_baseColor: new this.minigl.Uniform({
value: this.sectionColors[0],
type: "vec3",
excludeFrom: "fragment"
}),
u_waveLayers: new this.minigl.Uniform({
value: [],
excludeFrom: "fragment",
type: "array"
})
};
for (let e = 1; e < this.sectionColors.length; e += 1) this.uniforms.u_waveLayers.value.push(new this.minigl.Uniform({
value: {
color: new this.minigl.Uniform({
value: this.sectionColors[e],
type: "vec3"
}),
noiseFreq: new this.minigl.Uniform({
value: [2 + e / this.sectionColors.length, 3 + e / this.sectionColors.length],
type: "vec2"
}),
noiseSpeed: new this.minigl.Uniform({
value: 11 + .3 * e
}),
noiseFlow: new this.minigl.Uniform({
value: 6.5 + .3 * e
}),
noiseSeed: new this.minigl.Uniform({
value: this.seed + 10 * e
}),
noiseFloor: new this.minigl.Uniform({
value: .1
}),
noiseCeil: new this.minigl.Uniform({
value: .63 + .07 * e
})
},
type: "struct"
}));
return this.vertexShader = [this.shaderFiles.noise, this.shaderFiles.blend, this.shaderFiles.vertex].join("\n\n"), new this.minigl.Material(this.vertexShader, this.shaderFiles.fragment, this.uniforms)
}
initMesh() {
this.material = this.initMaterial(), this.geometry = new this.minigl.PlaneGeometry, this.mesh = new this.minigl.Mesh(this.geometry, this.material)
}
shouldSkipFrame(e) {
return !!window.document.hidden || (!this.conf.playing || (parseInt(e, 10) % 2 == 0 || void 0))
}
updateFrequency(e) {
this.freqX += e, this.freqY += e
}
toggleColor(index) {
this.activeColors[index] = 0 === this.activeColors[index] ? 1 : 0
}
showGradientLegend() {
this.width > this.minWidth && (this.isGradientLegendVisible = !0, document.body.classList.add("isGradientLegendVisible"))
}
hideGradientLegend() {
this.isGradientLegendVisible = !1, document.body.classList.remove("isGradientLegendVisible")
}
init() {
this.initGradientColors(), this.initMesh(), this.resize(), requestAnimationFrame(this.animate), window.addEventListener("resize", this.resize)
}
/*
* Waiting for the css variables to become available, usually on page load before we can continue.
* Using default colors assigned below if no variables have been found after maxCssVarRetries
*/
waitForCssVars() {
if (this.computedCanvasStyle && -1 !== this.computedCanvasStyle.getPropertyValue("--gradient-color-1").indexOf("#")) this.init(), this.addIsLoadedClass();
else {
if (this.cssVarRetries += 1, this.cssVarRetries > this.maxCssVarRetries) {
return this.sectionColors = [16711680, 16711680, 16711935, 65280, 255],void this.init();
}
requestAnimationFrame(() => this.waitForCssVars())
}
}
/*
* Initializes the four section colors by retrieving them from css variables.
*/
initGradientColors() {
this.sectionColors = ["--gradient-color-1", "--gradient-color-2", "--gradient-color-3", "--gradient-color-4"].map(cssPropertyName => {
let hex = this.computedCanvasStyle.getPropertyValue(cssPropertyName).trim();
//Check if shorthand hex value was used and double the length so the conversion in normalizeColor will work.
if (4 === hex.length) {
const hexTemp = hex.substr(1).split("").map(hexTemp => hexTemp + hexTemp).join("");
hex = `#${hexTemp}`
}
return hex && `0x${hex.substr(1)}`
}).filter(Boolean).map(normalizeColor)
}
}
/*
*Finally initializing the Gradient class, assigning a canvas to it and calling Gradient.connect() which initializes everything,
* Use Gradient.pause() and Gradient.play() for controls.
*
* Here are some default property values you can change anytime:
* Amplitude: Gradient.amp = 0
* Colors: Gradient.sectionColors (if you change colors, use normalizeColor(#hexValue)) before you assign it.
*
*
* Useful functions
* Gradient.toggleColor(index)
* Gradient.updateFrequency(freq)
*/
var gradient = new Gradient();
gradient.initGradient("#gradient-canvas");
</script>
<script type="text/javascript" src="/library/js/freja-eid.js?v=1787322557"></script>
<script type="text/javascript">
var _durkey = window.location.pathname+''+window.location.search;
if(_duration[_durkey] == undefined){
_duration[_durkey] = [];
}
_duration[_durkey].push(0.099011898040771);
console.log('SERVER :: 0.099011898040771');
console.log('SITE_ID :: ');
console.log('ZONE :: ');
console.log('QUERYS :: 0');
console.log('REDIS KEY :: '+settings['redis_key']);
console.log('VERSION :: 8.3.16');
console.log('SERVER :: dash');
if(typeof socket != 'undefined'){
socket.emit('loadtime',{site_id:settings.site_id,time:'0.099011898040771',page:_durkey});
}
</script> </main>
<footer id="page-footer" class="bg-white opacity-0">
</footer>
</div>
<div class="modal fade align-items-center justify-content-center" id="vvModal" role="dialog">
<div class="modal-dialog" role="document">
<button type="button" class="btn-block-option" data-dismiss="modal" aria-label="Close">
<i class="fal fa-times"></i>
</button>
<div class="modal-content ">
</div>
</div>
</div>
<div class="modal fade" id="vvExtraModal" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<button type="button" class="btn-block-option vv-modal-close" data-dismiss="modal" aria-label="Close">
<i class="fal fa-times"></i>
</button>
<div class="vv-modal-inner"></div>
</div>
</div>
</div>
<style type="text/css">
/*
* Shared card treatment for #notificationAckModal (#3234), #vvExtraModal
* (#3237) and #vvModal's settings confirm dialog (#3292, .vv-confirm):
* one definition, scoped to all three, so the three popups can't drift
* apart. Rules specific to one modal's own markup (ack's toggle/actions,
* vvExtraModal's media icon, the confirm dialog's icon/actions row) live
* next to that markup instead.
*
* #vvModal is a shared shell with 35 usages across 9 files (#3292) - it
* was deliberately left out of the #3234/#3237 redesign for exactly that
* reason. .vv-confirm is a marker class on the markup vvConfirm() in
* vvGeneral.js injects INSIDE #vvModal .modal-content, so every rule
* below that targets it is scoped to that injected content, never to
* the shell itself - the other 34 consumers never render a .vv-confirm
* and are untouched.
*/
#notificationAckModal .modal-content,
#vvExtraModal .modal-content,
#vvModal .vv-confirm{
position:relative;
padding:2rem 2rem 1.75rem;
border-radius:1rem!important;
border:none;
box-shadow:0 20px 50px -24px rgba(0,0,0,0.35),0 2px 8px -2px rgba(0,0,0,0.08);
overflow:hidden;
min-height:auto;
background:var(--true-mono-0);
}
/*
* #vvModal's own .modal-content (the shared shell) carries its own
* default card box - background, border, radius, padding, shadow - for
* every other consumer. With .vv-confirm rendering that same box one
* level in, keeping both would nest a card inside a card.
*
* Neutralising the shell is scoped to the .vv-confirm-active class,
* not :has(.vv-confirm) - Firefox only shipped :has() in version 121,
* and TRUE's customers are largely education, where Firefox ESR 115
* is common; matching on :has() would ship this dialog completely
* unstyled for them. vvConfirm() (vvGeneral.js) adds .vv-confirm-active
* to .modal-content and .modal-dialog itself when it opens, and removes
* it on #vvModal's hidden.bs.modal - see the comment there for why that
* removal matters. Every other #vvModal usage never gets this class and
* keeps its default box untouched, exactly as it renders today.
*/
#vvModal .modal-content.vv-confirm-active{
background:transparent;
border:none;
border-radius:0;
padding:0;
box-shadow:none;
min-height:0;
}
#vvModal .modal-dialog.vv-confirm-active{
max-width:460px;
}
#notificationAckModal .modal-content:before,
#vvExtraModal .modal-content:before,
#vvModal .vv-confirm:before{
content:"";
position:absolute;
top:0;left:0;right:0;
height:4px;
}
#notificationAckModal .modal-content:before,
#vvExtraModal .modal-content:before{
background:linear-gradient(90deg,var(--true-teal-500),var(--true-cerise-500));
}
#notificationAckModal .vv-modal-close,
#vvExtraModal .vv-modal-close,
#vvModal .vv-confirm .vv-modal-close{
position:absolute;
top:10px;right:10px;
z-index:2;
width:40px;height:40px;
display:flex;align-items:center;justify-content:center;
border-radius:50%;
color:var(--true-mono-300);
background:transparent;
border:none;
cursor:pointer;
transition:background-color .15s ease-out,color .15s ease-out;
}
#notificationAckModal .vv-modal-close:hover,
#notificationAckModal .vv-modal-close:focus,
#vvExtraModal .vv-modal-close:hover,
#vvExtraModal .vv-modal-close:focus,
#vvModal .vv-confirm .vv-modal-close:hover,
#vvModal .vv-confirm .vv-modal-close:focus{
color:var(--true-mono-500);
background-color:var(--true-mono-100);
outline:none;
}
#notificationAckModal .vv-modal-close:focus-visible,
#vvExtraModal .vv-modal-close:focus-visible,
#vvModal .vv-confirm .vv-modal-close:focus-visible{
box-shadow:0 0 0 3px var(--true-blue-200);
}
/*
* The confirm dialog's own close button lives inside .vv-confirm, same
* treatment as the other two modals. #vvModal's shell already renders
* ITS OWN close button outside .modal-content (footer.php, shared by
* all 35 consumers) - shown here only so the rule below reads clearly;
* hiding it is scoped the same way, via .vv-confirm-active on
* .modal-dialog, so no other consumer's close button is affected.
*/
#vvModal .modal-dialog.vv-confirm-active > .btn-block-option{
display:none;
}
#notificationAckModal .ack-title,
#vvExtraModal .alert-modal-body h2,
#vvModal .vv-confirm-title{
font:var(--true-textlarge-regular);
font-weight:600;
letter-spacing:-0.01em;
color:var(--true-mono-500);
margin:0 2rem 0.75rem 0;
}
#notificationAckModal .ack-message,
#vvExtraModal .alert-modal-body,
#vvModal .vv-confirm-body{
font:var(--true-textmedium-regular);
line-height:1.6;
color:var(--true-mono-400);
margin:0;
padding-bottom:0;
word-wrap:break-word;
}
#notificationAckModal .ack-message p,
#vvExtraModal .alert-modal-body p,
#vvModal .vv-confirm-body p{
margin:0 0 0.75rem;
}
#notificationAckModal .ack-message p:last-child,
#vvExtraModal .alert-modal-body p:last-child,
#vvModal .vv-confirm-body p:last-child{
margin-bottom:0;
}
#notificationAckModal .ack-message ul,
#notificationAckModal .ack-message ol,
#vvExtraModal .alert-modal-body ul,
#vvExtraModal .alert-modal-body ol{
margin:0 0 0.75rem;
padding-left:1.25rem;
}
/*
* Prose links inside the message body. The :not() is load bearing
* (#3252): in #vvExtraModal the action button arrives INSIDE
* .alert-modal-body, because it comes in as part of the
* #note-<id>-text block doModal() reads. Without the exclusion this
* rule matched it - and beat the button's own text-decoration:none
* on specificity, one id + one class + one element against one id +
* one class - so the button rendered as an underlined body link
* that happened to have a border round it, and turned cerise on
* hover. Excluding it here is better than escalating the button's
* own selectors, which would leave the two modals styled by
* different-looking rules for no reason.
*/
#notificationAckModal .ack-message a:not(.ack-link-btn),
#vvExtraModal .alert-modal-body a:not(.ack-link-btn){
color:var(--true-teal-500);
text-decoration:underline;
text-underline-offset:2px;
}
#notificationAckModal .ack-message a:not(.ack-link-btn):hover,
#vvExtraModal .alert-modal-body a:not(.ack-link-btn):hover{
color:var(--true-cerise-500);
}
@media (max-width:480px){
#notificationAckModal .modal-content,
#vvExtraModal .modal-content,
#vvModal .vv-confirm{
padding:1.5rem 1.25rem 1.25rem;
}
#notificationAckModal .vv-modal-close,
#vvExtraModal .vv-modal-close,
#vvModal .vv-confirm .vv-modal-close{
top:6px;right:6px;
}
}
/* #vvExtraModal-only: dialog width, brand icon, notification timestamp.
Content-agnostic - also used by the domains info popup. */
#vvExtraModal .modal-dialog{
max-width:560px;
}
#vvExtraModal .vv-modal-icon{
display:block;
width:40px;
margin:0 0 1rem;
}
#vvExtraModal .alert-modal-body .notification-time{
font:var(--true-textextrasmall-medium);
text-transform:uppercase;
letter-spacing:0.02em;
color:var(--true-mono-300);
margin:0 0 0.5rem;
}
#vvExtraModal .alert-modal-body img{
max-width:100%;
height:auto;
border-radius:0.5rem;
margin:0.5rem 0;
}
/*
* The notification's own image, as a lockup above the title
* (issue #3252). Shared by both modals because it is one element
* rendered by one helper, vvNotificationLockup().
*
* In #vvExtraModal it lands INSIDE .alert-modal-body (it arrives as
* part of the #note-<id>-text block doModal() reads), so it would
* otherwise pick up the generic content-image rule directly above -
* hence the deliberately heavier selector here, which wins on
* specificity rather than on source order.
*
* The size cap is the point of these rules. This is a media upload
* field, so it will be handed a 2000px image sooner or later, and
* this is a small mark above a heading - not a hero banner. BOTH
* axes are capped, with width/height:auto, so the aspect ratio is
* preserved and whichever cap binds first is decided by the image's
* own ratio against 200/38: a banner wider than that hits
* max-width, anything else hits max-height and the other axis just
* follows. Measured in the preview: a 2000x600 upload lands 127x38
* and a 600x2000 one lands 11x38. Neither can widen the card.
*/
#notificationAckModal .notification-lockup,
#vvExtraModal .notification-lockup{
margin:0 0 1rem;
line-height:0;
}
#notificationAckModal .notification-lockup img.notification-lockup-img,
#vvExtraModal .notification-lockup img.notification-lockup-img{
display:block;
width:auto;
height:auto;
max-width:200px;
max-height:38px;
/* A brand mark is not a content image: no rounded corners and
no vertical rhythm of its own, the wrapper owns the spacing. */
border-radius:0;
margin:0;
}
/*
* A notification with its own image brings its own branding, so it
* replaces the generic TRUE gif that doModal() hardcodes into
* #vvExtraModal - showing both would be two logos stacked. With no
* image the gif stays exactly as it is today, and the ack modal,
* which never had a gif, simply shows nothing. Done here in CSS
* precisely so doModal() itself does not have to change - the gif
* is hardcoded in that function, and reaching in to make it
* conditional would mean changing behaviour this issue is meant to
* leave alone. A browser without :has() support drops this one rule
* and shows both marks, which is untidy rather than broken.
*/
#vvExtraModal .vv-modal-inner:has(.notification-lockup) .vv-modal-icon{
display:none;
}
/*
* The action button (issue #3252). Everything about how the button
* LOOKS lives here, scoped to both ids, because both modals render
* the same button - the one #3239 introduced and #3247 tuned. Only
* how it participates in the acknowledgement modal's two-button row
* stays with that row, in the ack-modal-only block further down.
*
* Outline treatment keeps it visibly lower-emphasis than the ack
* modal's Confirm, so people don't navigate away instead of
* acknowledging. In the bell popup it is the only button, and the
* same treatment reads correctly there too: it is an offer, not a
* demand.
*
* The width comes from the label (flex:0 1 auto, no max-width), so
* a label that fits stays on ONE line. It had a max-width:220px,
* which is what wrapped "Get started with TRUE Sign" after "TRUE"
* while the row still had visible empty space (#3247); a fixed cap
* is guesswork about the next label.
*
* Only when the row genuinely needs the space does the label wrap,
* up to two lines rather than truncating to one - a clipped label
* tells the user nothing. Past two lines it clamps with an
* ellipsis; that needs left-aligned text, since
* text-overflow:ellipsis never renders on centred text (the
* overflow spills both ways and the ellipsis itself ends up
* off-box). The clamp lives on the inner .ack-link-btn-label span,
* not the flex item itself - -webkit-line-clamp silently fails to
* constrain height when applied directly to a flex item (verified
* by rendering both ways; the direct version overflows its own
* border uncontained).
*
* #3292: the settings confirm dialog's Cancel button reuses this same
* definition (an outline secondary, one visual language, not a fourth
* button style) rather than the near-invisible transparent-border
* .btn-secondary it shipped with. Cancel has no two-line label to
* clamp, so it skips .ack-link-btn-label and just takes the base rule.
*/
#notificationAckModal .ack-link-btn,
#vvExtraModal .ack-link-btn,
#vvModal .vv-confirm .ack-link-btn{
display:flex;
align-items:center;
flex:0 1 auto;
min-width:0;
padding:0.75rem 1.5rem;
border-radius:0.6rem;
border:1px solid var(--true-mono-200);
color:var(--true-teal-500);
background-color:transparent;
text-decoration:none;
transition:background-color .15s ease-out,border-color .15s ease-out,transform .1s ease-out;
}
#notificationAckModal .ack-link-btn-label,
#vvExtraModal .ack-link-btn-label{
display:-webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:2;
flex:1 1 auto;
min-width:0;
font:var(--true-textsmall-medium);
font-weight:600;
text-align:left;
overflow:hidden;
overflow-wrap:break-word;
text-overflow:ellipsis;
}
#notificationAckModal .ack-link-btn:hover,
#notificationAckModal .ack-link-btn:focus,
#vvExtraModal .ack-link-btn:hover,
#vvExtraModal .ack-link-btn:focus,
#vvModal .vv-confirm .ack-link-btn:hover,
#vvModal .vv-confirm .ack-link-btn:focus{
border-color:var(--true-teal-500);
background-color:var(--true-teal-100);
color:var(--true-teal-500);
text-decoration:none;
}
#notificationAckModal .ack-link-btn:focus-visible,
#vvExtraModal .ack-link-btn:focus-visible,
#vvModal .vv-confirm .ack-link-btn:focus-visible{
outline:none;
box-shadow:0 0 0 3px var(--true-blue-200);
}
#notificationAckModal .ack-link-btn:active,
#vvExtraModal .ack-link-btn:active,
#vvModal .vv-confirm .ack-link-btn:active{
transform:translateY(1px);
}
/*
* The bell popup has no Confirm, so the action button is alone.
* Giving it its own row anchored to the left of the card - rather
* than letting it sit inline at the end of the message text - is
* what stops it reading as floating. The top margin matches the ack
* modal's .ack-actions so the button sits the same distance below
* the body in both. The row is only emitted when there is a button,
* so no URL leaves no empty row behind. The row lands inside
* .alert-modal-body, whose prose-link rule had to be taught to skip
* the button explicitly - see the :not(.ack-link-btn) above.
*/
#vvExtraModal .notification-action-row{
display:flex;
justify-content:flex-start;
margin-top:1.5rem;
}
@media (max-width:480px){
/* Same full-width treatment the ack modal's stacked row gets
below, so the lone button in the popup doesn't end up as a
narrow tab on a narrow screen. */
#vvExtraModal .ack-link-btn{
width:100%;
max-width:none;
}
}
/*
* #vvModal .vv-confirm-only (#3292): the settings confirm dialog
* vvConfirm() in vvGeneral.js builds. Everything shared with the other
* two modals - the card shell, close button, title and body type, and
* Cancel's outline-secondary treatment - is defined once above.
*
* align-items:baseline, not center: the title can wrap to two or
* three lines (see the 360px panel), and centering the icon
* against the WHOLE multi-line block floats it beside whichever
* line happens to be in the middle - the same alignment bug the
* #3234 toggle row had on one line and not the others. Flexbox's
* "first baseline" of a multi-line item is the baseline of its
* FIRST line specifically, so this pins the icon to the first
* line regardless of how many the title wraps to.
*/
#vvModal .vv-confirm-title{
display:flex;
align-items:baseline;
}
/*
* The warning triangle / info circle. mr-2 (2px) is what this dialog
* shipped with - a real utility class, just the wrong one on this
* scale, so the icon read as glued to the title. mr-10 (~10px) is the
* spacing the rest of this dialog's own markup already uses.
*/
#vvModal .vv-confirm-icon{
flex-shrink:0;
margin-right:0.625rem;
font-size:1rem;
color:var(--true-danger-500);
}
/*
* The accent hairline carries the meaning (#3292): danger for the
* default warning style - this is a destructive confirm, not an
* announcement - and a calmer info-blue for data-confirm-style="info".
* Both read as clearly distinct from each other and from the
* teal-to-cerise brand gradient the two announcement modals use above,
* so nobody mistakes a destructive confirm for an FYI.
*/
#vvModal .vv-confirm:before{
background:var(--true-danger-500);
}
#vvModal .vv-confirm.vv-confirm--info:before{
background:var(--true-blue-500);
}
#vvModal .vv-confirm.vv-confirm--info .vv-confirm-icon{
color:var(--true-blue-500);
}
#vvModal .vv-confirm-body{
margin-bottom:1.5rem;
}
/*
* Cancel and the destructive/confirm action end the card as a pair,
* flush right - unlike the ack modal's row, nothing here is a lower-
* priority offer anchored to the far left, so there is no reason to
* spread them apart. Same left-to-right order the dialog already
* shipped with: Cancel, then the confirm action last, at the row's
* far end - keeping full btn-danger/btn-info weight so it never reads
* quieter than Cancel (#3292). Stacked on mobile that puts it last in
* the column too, nearest the thumb.
*/
#vvModal .vv-confirm-actions{
display:flex;
justify-content:flex-end;
align-items:center;
gap:0.75rem;
}
@media (max-width:480px){
#vvModal .vv-confirm-actions{
flex-direction:column;
align-items:stretch;
}
#vvModal .vv-confirm-actions .btn{
width:100%;
}
}
</style>
<div class="modal fade" id="confirmDialog" tabindex="-1" role="dialog" aria-labelledby="confirmDialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Warning</h4>
</div>
<div class="modal-body">
Edit in progress and if you close you will lose unsaved information. Do you want to close anyway? </div>
<div class="modal-footer">
<div class="btn btn-primary btn-okey">ok</div>
<div class="btn btn-primary btn-cancel">cancel</div>
</div>
</div>
</div>
</div>
<script src="/site/_codebase/assets/js/plugins/select2/js/select2.full.min.js"></script>
<link rel="stylesheet" href="/site/_codebase/assets/js/plugins/magnific-popup/magnific-popup.css">
<script src="/site/_codebase/assets/js/plugins/magnific-popup/jquery.magnific-popup.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous"></script>
<!-- Include the Stripe.js library -->
<script src="https://js.stripe.com/v3/"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/core/jquery.slimscroll.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/core/jquery-scrollLock.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/core/jquery.appear.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/core/jquery.countTo.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/core/js.cookie.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/codebase.js?v=1787322557"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/slick/slick.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/chartjs/Chart.bundle.min.js?v=3.16"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-notify/bootstrap-notify.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/datatables/jquery.dataTables.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/datatables/dataTables.bootstrap4.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-wizard/jquery.bootstrap.wizard.min.js"></script>
<link rel="stylesheet" href="//static.trueoriginal.com/site/_codebase/assets/js/plugins/highlightjs/styles/github-gist.css">
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/highlightjs/highlight.pack.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-datepicker/js/bootstrap-datepicker.min.js"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/flatpickr/flatpickr.js?v=1787322557"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/flatpickr/plugins/confirmDate/confirmDate.js"></script>
<script src="//static.trueoriginal.com/mini/flatpickr.sv.js?v=1787322557"></script>
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/pwstrength-bootstrap/pwstrength-bootstrap.min.js"></script>
<link rel="stylesheet" href="//static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-datepicker/css/bootstrap-datepicker3.min.css">
<link rel="stylesheet" href="//static.trueoriginal.com/site/_codebase/assets/js/plugins/flatpickr/flatpickr.min.css">
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/ion-rangeslider/js/ion.rangeSlider.min.js?v=2.3"></script>
<link rel="stylesheet" href="//static.trueoriginal.com/site/_codebase/assets/js/plugins/ion-rangeslider/css/ion.rangeSlider.css">
<script src="//static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-colorpicker/js/bootstrap-colorpicker.min.js"></script>
<link rel="stylesheet" href="//static.trueoriginal.com/site/_codebase/assets/js/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css">
<!-- TrueEditor - Custom rich text editor -->
<script src="//static.trueoriginal.com/mini/true-editor.js?v=1787322557"></script>
<script src="//static.trueoriginal.com/mini/codeflask.min.js?v=1787322557"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/fontawesome-iconpicker@3.2.0/dist/js/fontawesome-iconpicker.min.js"></script> -->
<script src="https://kit.fontawesome.com/1843dbe3fb.js" crossorigin="anonymous"></script>
<script type="text/javascript" src="//static.trueoriginal.com/mini/vvGeneral.js?v=1787322557"></script>
<script type="text/javascript" src="//static.trueoriginal.com/mini/vvjQuery.js?v=1787322557"></script>
<script type="text/javascript" src="//static.trueoriginal.com/mini/vvAppend.js?v=1787322557" ></script>
<script type="text/javascript" src="//static.trueoriginal.com/mini/dropzone/dropzone.js?v=1787322557" ></script>
<script src="//static.trueoriginal.com/mini/vvPoll.js?v=1787322557"></script>
<script src="//static.trueoriginal.com/mini/vvUpdate.js?v=1787322557"></script>
<script src="/mini/vvUpload.js?v=1787322557"></script>
<script src="/mini/sweetalert.min.js?v=1787322557"></script>
<link rel="stylesheet" href="/library/css/ai-describe.css?r=1&v=1787322557">
<script type="text/javascript" src="//static.trueoriginal.com/mini/ai-describe.js?v=1787322557" ></script>
<script type="text/javascript" src="/mini/true-app.js?v=1787322557" ></script>
<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"1148dfe429164366a4e4aabdbcdcc155"}' crossorigin="anonymous"></script>
</body>
</html></body></html><!-- pretime: 0.099174022674561s -->
Sitemap
Кол-во: 0
XML-карта сайта для поисковиков
?
Sitemap.xml помогает поисковику быстрее находить и индексировать страницы. Особенно важен для крупных сайтов и новых страниц, на которые ещё нет входящих ссылок.
Robots.txt не содержит ссылку на карту сайта. Рекомендуется добавить карту сайта и указать ссылку на нее в robots.txt.
Внутренние ссылки
Не найдено
Ссылки на другие страницы своего сайта
?
Внутренние ссылки распределяют ссылочный вес между страницами и помогают поисковику обходить сайт. Пустые анкоры и ссылки на запрещённые robots.txt страницы — типичные ошибки.
Внутренних ссылок на странице 0 оптимально.
Внешние ссылки
Не найдено
Ссылки на сторонние сайты
?
Исходящие внешние ссылки передают часть ссылочного веса на чужие сайты. Ссылки на авторитетные ресурсы безопасны; ссылки на мусорные сайты могут навредить репутации страницы.
Внешних ссылок на странице не найдено.
Конкуренты Готовность: 0%
Конкуренты в Яндексе
Кол-во: 0
Топ сайтов-конкурентов в Яндексе
?
Сайты, чаще всего появляющиеся в ТОПе Яндекса по запросам из семантического ядра этой страницы.
Мы не нашли у вас конкурентов в Яндексе. Сайт или очень молодой или плохо продвигается.
Конкурентов в ТОП-10 Яндекса не нашлось.
Конкуренты в Google
Кол-во: 0
Топ сайтов-конкурентов в Google
?
Сайты, чаще всего появляющиеся в ТОПе Google по запросам из семантического ядра этой страницы.
Конкуренты в Google тоже не найдены. Займитесь продвижением сайта!
Конкурентов в ТОП-10 Google не нашлось.
ЗоЗПП: права потребителей Готовность: 100%
Нарушения
Не выявлены
Признаков дистанционной продажи товаров (интернет-магазина) не обнаружено — требования ЗоЗПП о раскрытии информации продавца к сайту не применяются. Нарушений нет.
ФЗ-149: рекомендательные технологии Готовность: 100%
Нарушения
Не выявлены
Рекомендательные блоки («с этим покупают», «похожие товары» и т.п.) на сайте не обнаружены — требования ст. 10.7 ФЗ-149 к сайту не применяются. Нарушений нет.
ФЗ-38: реклама Готовность: 100%
Нарушения
Не выявлены
Рекламных тематик с обязательными оговорками (медицина, БАД, кредиты и займы, новостройки) на сайте не обнаружено. Нарушений нет.
ФЗ-436: защита детей Готовность: 100%
Нарушения
Не выявлены
Признаков информационной продукции (новости, видео, книги, игры, курсы) не обнаружено — обязательная возрастная маркировка по ФЗ-436 сайту не требуется. Нарушений нет.
Вердикт
Анализ сайта dash.trueoriginal.com, слабо оптимизирован на 44%. Для хорошей оптимизации и выхода на первые места в поиске требуется:
Постарайтесь исправить общие ошибки.
Исправьте ошибки в мета-тегах.
Исправьте ошибки индексации.
Поделитесь с друзьями: