| Server IP : 138.197.107.151 / Your IP : 216.73.217.7 Web Server : Apache/2.4.58 (Ubuntu) System : Linux BloxBy-Builder 6.8.0-71-generic #71-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 22 16:52:38 UTC 2025 x86_64 User : wpbetasites_mrakzqskir ( 1022) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/bsd-crawler-parser/viewer/templates/ |
Upload File : |
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}AI crawler viewer{% endblock %}</title>
<style>
:root {
/* BluShark blue, sampled from the logo (#00bcff, rgb 0,188,255).
It is a bright cyan: fine as a fill behind dark text (9.6:1) but far too
light for text on white (2.2:1), so links and small text use --brand-ink,
a darkened version of the same hue that clears 4.5:1. */
--brand:#00bcff;
--brand-ink:#0077aa;
--brand-wash:#e6f8ff;
--bg:#f5f7f9; --card:#fff; --ink:#16191d; --muted:#667085; --line:#e3e6ea;
--ai-training:#0077aa; --ai-assistant:#0f9d58; --ai-search:#6d28d9; --search:#98a2b3;
}
* { box-sizing:border-box; }
body { margin:0; background:var(--bg); color:var(--ink);
font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif; }
a { color:var(--brand-ink); }
header { background:var(--card); border-bottom:1px solid var(--line);
padding:0 22px; height:60px; display:flex; align-items:center; }
/* The logo is black+cyan artwork on transparency, so the bar stays white. */
header .logo { display:flex; align-items:center; height:100%; }
header .logo img { height:30px; width:auto; display:block; }
header nav { margin-left:auto; display:flex; gap:8px; align-items:center; }
header nav a { color:var(--muted); text-decoration:none; font-size:13px;
padding:6px 12px; border:1px solid var(--line); border-radius:7px; }
header nav a:hover { color:var(--ink); border-color:var(--muted); }
header nav a.on { color:var(--ink); border-color:var(--brand);
background:var(--brand-wash); font-weight:600; }
main { max-width:1220px; margin:22px auto; padding:0 22px; }
.card { background:var(--card); border:1px solid var(--line); border-radius:12px;
padding:18px; margin-bottom:16px; }
.card h2 { margin:0 0 14px; font-size:12px; text-transform:uppercase;
letter-spacing:.05em; color:var(--muted); font-weight:600; }
table { width:100%; border-collapse:collapse; }
th, td { text-align:left; padding:8px 10px; border-bottom:1px solid var(--line);
vertical-align:middle; }
th { font-size:11px; text-transform:uppercase; letter-spacing:.04em;
color:var(--muted); font-weight:600; }
td.num, th.num { text-align:right; font-variant-numeric:tabular-nums; }
tbody tr:last-child td { border-bottom:none; }
tbody tr:hover { background:#fafbfc; }
.tag { display:inline-block; padding:2px 8px; border-radius:99px; font-size:11px;
font-weight:600; white-space:nowrap; }
.tag.ai_training { background:#e0f2fe; color:#075985; }
.tag.ai_assistant { background:#dcfce7; color:#14532d; }
.tag.ai_search { background:#ede9fe; color:#4c1d95; }
.tag.search { background:#f1f3f5; color:#475467; }
/* ---- totals: two big boxes, then half-height boxes beside them ---- */
.totals { display:flex; gap:12px; align-items:stretch; }
/* Both big boxes use the same neutral treatment as the small ones -- dark
number on white -- so the row reads as one consistent set. */
.big { flex:0 0 190px; background:var(--card); border:1px solid var(--line);
border-radius:12px; padding:18px 20px; display:flex; flex-direction:column;
justify-content:center; }
.big .n { font-size:40px; line-height:1.05; font-weight:700;
font-variant-numeric:tabular-nums; color:var(--ink); }
.big .l { font-size:12px; font-weight:600; text-transform:uppercase;
letter-spacing:.06em; color:var(--muted); margin-top:4px;
display:flex; align-items:center; gap:5px; }
.big.sites { background:var(--brand); border-color:var(--brand); }
.big.hits { background:var(--line); }
/* The muted grey label only manages 2.3:1 on the brand blue, so darken it
here. The number is already near-black and reads at 9.6:1 either way. */
.big.sites .l { color:#00303f; }
/* "312" over "/354 Sites". The total rides on the label line but is sized and
weighted up so it reads as a number, not as wording. */
.big .l .of { font-size:14px; font-weight:700; letter-spacing:0;
font-variant-numeric:tabular-nums; color:var(--ink); }
.big.sites .l .of { color:#00303f; }
/* Two rows of half-height boxes that together match the big boxes. Four
small boxes sit 2x2; a fifth (Search engines) makes it 3+2. */
.grid { flex:1; display:grid; grid-template-columns:repeat(2, 1fr);
grid-template-rows:1fr 1fr; gap:10px; }
.grid.five { grid-template-columns:repeat(3, 1fr); }
.mini { border:1px solid var(--line); border-radius:10px; padding:10px 12px;
display:flex; flex-direction:column; justify-content:center; min-width:0; }
.mini .n { font-size:20px; font-weight:700; line-height:1.1;
font-variant-numeric:tabular-nums; }
.mini .l { font-size:10.5px; font-weight:600; text-transform:uppercase;
letter-spacing:.05em; color:var(--muted); margin-top:2px;
display:flex; align-items:center; gap:4px; white-space:nowrap; }
.mini.ai_training { border-left:3px solid var(--ai-training); }
.mini.ai_assistant { border-left:3px solid var(--ai-assistant); }
.mini.ai_search { border-left:3px solid var(--ai-search); }
.mini.search { border-left:3px solid var(--search); }
/* ---- info icon: solid black circle, lowercase white i ---- */
.info { display:inline-flex; align-items:center; justify-content:center;
width:14px; height:14px; border-radius:50%; background:#000; color:#fff;
font-size:10px; font-weight:700; font-style:normal; line-height:1;
font-family:Georgia,"Times New Roman",serif; text-transform:lowercase;
cursor:help; position:relative; flex:0 0 auto; }
.info:hover { background:#333; }
.info::after {
content:attr(data-tip); position:absolute; bottom:calc(100% + 8px); left:50%;
transform:translateX(-50%);
/* The label above sets white-space:nowrap, and a pseudo-element inherits
it -- which made the text run off in one long line and ignore the width.
Forcing it back to normal is what actually makes the bubble wrap. */
white-space:normal; overflow-wrap:break-word; word-break:normal;
width:max-content; max-width:260px;
background:#16191d; color:#fff; padding:9px 11px; border-radius:7px;
font:400 12px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
text-transform:none; letter-spacing:0; text-align:left; opacity:0;
visibility:hidden; transition:opacity .12s; z-index:20; pointer-events:none;
box-shadow:0 6px 18px rgba(0,0,0,.22);
}
.info::before {
content:""; position:absolute; bottom:calc(100% + 3px); left:50%;
transform:translateX(-50%); border:5px solid transparent; border-top-color:#16191d;
opacity:0; visibility:hidden; transition:opacity .12s; z-index:21;
}
.info:hover::after, .info:hover::before { opacity:1; visibility:visible; }
/* Keep the bubble on-screen rather than clipped at the left/right edges. */
td:first-child .info::after, .mini .info::after, .big .info::after {
left:0; transform:none;
}
tr:last-child .info::after, tr:last-child .info::before {
bottom:auto; top:calc(100% + 8px);
}
tr:last-child .info::before { border-top-color:transparent; border-bottom-color:#16191d; }
/* ---- filters ---- */
.filters { display:flex; flex-wrap:wrap; gap:14px 16px; align-items:end; }
.field { display:flex; flex-direction:column; gap:5px; min-width:0; }
.field > span { font-size:11px; font-weight:600; text-transform:uppercase;
letter-spacing:.04em; color:var(--muted); }
input, select { font:inherit; height:36px; padding:0 10px; border:1px solid var(--line);
border-radius:8px; background:#fff; color:var(--ink); }
input:focus, select:focus { outline:2px solid var(--brand); outline-offset:-1px;
border-color:var(--brand); }
select { padding-right:26px; }
.btn { height:36px; padding:0 16px; border-radius:8px; border:1px solid var(--brand);
background:var(--brand); color:#00303f; font:600 13px/1 inherit;
cursor:pointer; display:inline-flex; align-items:center; gap:6px;
text-decoration:none; white-space:nowrap; }
.btn:hover { filter:brightness(1.06); }
.btn.ghost { background:#fff; color:var(--ink); border-color:var(--line); }
.btn.ghost:hover { border-color:var(--muted); filter:none; }
/* A real checkbox aligned on its own baseline: the label sits level with the
box rather than floating above it, and the whole row is clickable. */
.checkbox { display:flex; align-items:center; gap:8px; height:36px;
padding:0 12px 0 10px; border:1px solid var(--line); border-radius:8px;
background:#fff; cursor:pointer; user-select:none; white-space:nowrap; }
.checkbox:hover { border-color:var(--muted); }
.checkbox input { height:16px; width:16px; margin:0; accent-color:var(--brand-ink);
cursor:pointer; flex:0 0 auto; }
.checkbox span { font-size:13px; line-height:1; }
.checkbox .sub { color:var(--muted); }
.bar { height:6px; background:var(--brand); border-radius:99px; min-width:2px; }
.bar.search { background:var(--search); }
.url { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px;
word-break:break-all; max-width:440px; }
.muted { color:var(--muted); }
.empty { padding:36px; text-align:center; color:var(--muted); }
.arrow { font-size:11px; text-decoration:none; }
.pager { display:flex; gap:6px; align-items:center; justify-content:center;
padding-top:14px; flex-wrap:wrap; }
.pager a, .pager button { padding:6px 11px; border-radius:7px;
border:1px solid var(--line); background:#fff; text-decoration:none;
font:inherit; color:var(--ink); cursor:pointer; }
.pager a:hover, .pager button:hover:not(:disabled) { border-color:var(--muted); }
.pager button:disabled { opacity:.4; cursor:default; }
.pager button.on { background:var(--brand); border-color:var(--brand);
color:#00303f; font-weight:700; }
.pager .gap { padding:6px 2px; color:var(--muted); }
.pager .cur { padding:6px 11px; color:var(--muted); }
/* A site with a log file but no AI traffic. Still a row -- it is a site we
cover -- but it should not compete with the sites that have hits. */
tr.zero td { color:var(--muted); }
tr.zero .bar { display:none; }
/* "+8": this install serves other domains and their hits are in this number. */
.more { display:inline-block; margin-left:6px; padding:1px 5px; border-radius:5px;
background:var(--line); color:var(--muted); font-size:11px;
font-weight:600; font-variant-numeric:tabular-nums; cursor:help; }
/* heading on the left, search + count pushed to the right, above a table */
.tablebar { display:flex; align-items:center; gap:12px; margin-bottom:12px;
flex-wrap:wrap; }
.tablebar h2 { margin:0; margin-right:auto; }
.tablebar input { flex:0 0 260px; }
.tablebar .count { font-size:12px; color:var(--muted); }
.note { background:#fffbeb; border:1px solid #fde68a; color:#78350f;
padding:10px 13px; border-radius:9px; font-size:13px; }
.rowline { display:flex; align-items:center; gap:7px; }
/* Login screen: one centered card, no header. Reuses the dashboard's card,
.field labels and .btn so it reads as the same app. */
body.login main { min-height:100vh; max-width:none; margin:0; padding:24px;
display:flex; align-items:center; justify-content:center; }
.login-card { width:100%; max-width:360px; }
.login-logo { display:flex; justify-content:center; margin-bottom:22px; }
.login-logo img { height:40px; width:auto; display:block; }
.login-card form { display:flex; flex-direction:column; gap:14px; }
.login-card .field input { width:100%; }
.login-card .btn { width:100%; justify-content:center; margin-top:2px; }
.login-note { margin:16px 0 0; font-size:12px; line-height:1.5;
color:var(--muted); text-align:center; }
/* Wrong-credentials message: red-tinted, distinct from the amber .note. */
.error { background:#fef2f2; border:1px solid #fecaca; color:#b42318;
padding:9px 12px; border-radius:8px; font-size:13px; margin-bottom:16px; }
</style>
</head>
<body class="{% block body_class %}{% endblock %}">
{% if session.get('user') %}
<header>
<a class="logo" href="{{ url_for('overview') }}" title="Dashboard">
<img src="{{ url_for('static', filename='blushark-digital.png') }}" alt="BluShark Digital">
</a>
{# keep() carries the current filters across pages, so switching view never
silently resets the date range or site you were looking at. #}
<nav>
<a class="{{ 'on' if request.endpoint == 'overview' }}"
href="{{ url_for('overview', **keep()) }}">Overview</a>
<a class="{{ 'on' if request.endpoint == 'detail' }}"
href="{{ url_for('detail', **keep()) }}">Detail</a>
<a class="{{ 'on' if request.endpoint == 'trend' }}"
href="{{ url_for('trend', **keep()) }}">URL trend</a>
<a href="{{ url_for('logout') }}">Log out</a>
</nav>
</header>
{% endif %}
<main>{% block body %}{% endblock %}</main>
</body>
</html>