fix inconsistencies in page layout, add swiss clock
This commit is contained in:
@@ -3,20 +3,13 @@
|
|||||||
{% include 'head.html' %}
|
{% include 'head.html' %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
|
||||||
margin: 20px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 { color: #333; margin-bottom: 10px; font-size: 1.5em; }
|
||||||
color: #333;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
color: #666;
|
color: #666;
|
||||||
|
|||||||
@@ -6,10 +6,24 @@
|
|||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
{% if extra_scripts %}<script src="{{ extra_scripts }}"></script>{% endif %}
|
{% if extra_scripts %}<script src="{{ extra_scripts }}"></script>{% endif %}
|
||||||
<style>
|
<style>
|
||||||
|
/* ── Reset / shared baseline ── */
|
||||||
|
*, *::before, *::after { box-sizing: border-box; }
|
||||||
|
html {
|
||||||
|
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
h1 { font-size: 1.5em; color: #333; margin: 0 0 5px; }
|
||||||
|
h2 { font-size: 1.1em; color: #333; margin: 0 0 8px; }
|
||||||
|
|
||||||
/* Navigation bar — shared across all pages */
|
/* Navigation bar — shared across all pages */
|
||||||
.nav {
|
.nav {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 10px 15px;
|
padding: 6px 12px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,.1);
|
box-shadow: 0 2px 4px rgba(0,0,0,.1);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -42,6 +56,17 @@
|
|||||||
transition: background 0.15s;
|
transition: background 0.15s;
|
||||||
}
|
}
|
||||||
.nav-user:hover { background: #f0f4ff; text-decoration: none; }
|
.nav-user:hover { background: #f0f4ff; text-decoration: none; }
|
||||||
|
.nav-username {
|
||||||
|
max-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0;
|
||||||
|
transition: max-width 0.2s ease, opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
.nav-user:hover .nav-username {
|
||||||
|
max-width: 160px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
.nav-avatar {
|
.nav-avatar {
|
||||||
width: 28px; height: 28px;
|
width: 28px; height: 28px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -94,6 +119,158 @@
|
|||||||
.nav-links.nav-open { display: flex; }
|
.nav-links.nav-open { display: flex; }
|
||||||
.nav-links a { margin-right: 0; padding: 6px 0; font-size: 1em; }
|
.nav-links a { margin-right: 0; padding: 6px 0; font-size: 1em; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Swiss railway clock — nav */
|
||||||
|
.nav-clock {
|
||||||
|
flex-shrink: 0;
|
||||||
|
line-height: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
padding: 4px 4px 4px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#swiss-clock { display: block; }
|
||||||
|
|
||||||
|
/* Swiss railway clock — full-page overlay */
|
||||||
|
#clock-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
background: #1a1a1a;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#clock-overlay.visible { display: flex; }
|
||||||
|
#swiss-clock-overlay { display: block; }
|
||||||
</style>
|
</style>
|
||||||
|
<script>
|
||||||
|
/* ── Swiss Federal Railway (SBB) clock ── */
|
||||||
|
|
||||||
|
/* Draw one frame of the clock onto any canvas element. */
|
||||||
|
function drawSwissClock(canvas) {
|
||||||
|
var SIZE = canvas.width;
|
||||||
|
var R = SIZE / 2;
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
var now = new Date();
|
||||||
|
var h = now.getHours() % 12;
|
||||||
|
var m = now.getMinutes();
|
||||||
|
var s = now.getSeconds();
|
||||||
|
var ms = now.getMilliseconds();
|
||||||
|
|
||||||
|
/* Seconds hand idles ~1.5 s at 12 before advancing (SBB behaviour) */
|
||||||
|
var sFrac = s + ms / 1000;
|
||||||
|
var sAngle = sFrac >= 58.5 ? 0 : (sFrac / 58.5) * Math.PI * 2;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, SIZE, SIZE);
|
||||||
|
|
||||||
|
/* face */
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(R, R, R - 1, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = '#333';
|
||||||
|
ctx.lineWidth = SIZE * 0.018;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
/* tick marks */
|
||||||
|
for (var i = 0; i < 60; i++) {
|
||||||
|
var a = (i / 60) * Math.PI * 2 - Math.PI / 2;
|
||||||
|
var isHour = (i % 5 === 0);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(R + Math.cos(a) * (isHour ? R * 0.72 : R * 0.88),
|
||||||
|
R + Math.sin(a) * (isHour ? R * 0.72 : R * 0.88));
|
||||||
|
ctx.lineTo(R + Math.cos(a) * R * 0.94,
|
||||||
|
R + Math.sin(a) * R * 0.94);
|
||||||
|
ctx.strokeStyle = '#222';
|
||||||
|
ctx.lineWidth = isHour ? SIZE * 0.027 : SIZE * 0.011;
|
||||||
|
ctx.lineCap = 'butt';
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hands */
|
||||||
|
function hand(angle, tip, tail, width, color) {
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(R, R);
|
||||||
|
ctx.rotate(angle);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(tail, 0);
|
||||||
|
ctx.lineTo(tip, 0);
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.lineWidth = width;
|
||||||
|
ctx.lineCap = 'square';
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
hand((m + s / 60) / 60 * Math.PI * 2 - Math.PI / 2,
|
||||||
|
R * 0.88, -R * 0.12, SIZE * 0.027, '#222'); /* minute */
|
||||||
|
hand((h + m / 60) / 12 * Math.PI * 2 - Math.PI / 2,
|
||||||
|
R * 0.58, -R * 0.12, SIZE * 0.039, '#222'); /* hour */
|
||||||
|
hand(sAngle - Math.PI / 2, R * 0.78, -R * 0.22,
|
||||||
|
SIZE * 0.013, '#e00'); /* second tail+tip */
|
||||||
|
|
||||||
|
/* round dot at tip of second hand */
|
||||||
|
var dotR = SIZE * 0.028;
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(R, R);
|
||||||
|
ctx.rotate(sAngle - Math.PI / 2);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(R * 0.78, 0, dotR, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#e00';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
/* centre cap */
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(R, R, R * 0.04, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#222';
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resize the overlay canvas to fit the viewport, keeping it square. */
|
||||||
|
function resizeOverlayClock() {
|
||||||
|
var oc = document.getElementById('swiss-clock-overlay');
|
||||||
|
if (!oc) return;
|
||||||
|
var size = Math.min(window.innerWidth, window.innerHeight) * 0.88;
|
||||||
|
size = Math.floor(size);
|
||||||
|
oc.width = size;
|
||||||
|
oc.height = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main tick — redraws both nav clock and (if visible) overlay clock. */
|
||||||
|
function clockTick() {
|
||||||
|
var nav = document.getElementById('swiss-clock');
|
||||||
|
if (nav) drawSwissClock(nav);
|
||||||
|
var overlay = document.getElementById('clock-overlay');
|
||||||
|
if (overlay && overlay.classList.contains('visible')) {
|
||||||
|
var oc = document.getElementById('swiss-clock-overlay');
|
||||||
|
if (oc) drawSwissClock(oc);
|
||||||
|
}
|
||||||
|
var delay = 100 - (Date.now() % 100);
|
||||||
|
setTimeout(clockTick, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
/* Start the shared tick loop */
|
||||||
|
clockTick();
|
||||||
|
|
||||||
|
/* Overlay toggle — clicking the nav clock opens it */
|
||||||
|
var navClock = document.querySelector('.nav-clock');
|
||||||
|
var overlay = document.getElementById('clock-overlay');
|
||||||
|
if (navClock && overlay) {
|
||||||
|
navClock.addEventListener('click', function() {
|
||||||
|
resizeOverlayClock();
|
||||||
|
overlay.classList.add('visible');
|
||||||
|
});
|
||||||
|
overlay.addEventListener('click', function() {
|
||||||
|
overlay.classList.remove('visible');
|
||||||
|
});
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
if (overlay.classList.contains('visible')) resizeOverlayClock();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script src="static/sorttable.js"></script>
|
<script src="static/sorttable.js"></script>
|
||||||
</head>
|
</head>
|
||||||
@@ -7,10 +7,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 0;
|
|
||||||
background: #f5f5f5;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
<a href="/settings"{% if active_page == "settings" %} class="active"{% endif %}>Settings</a>
|
<a href="/settings"{% if active_page == "settings" %} class="active"{% endif %}>Settings</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="nav-clock" title="Click for full-screen clock">
|
||||||
|
<canvas id="swiss-clock" width="44" height="44"></canvas>
|
||||||
|
</div>
|
||||||
{% if current_user %}
|
{% if current_user %}
|
||||||
<a href="/profile" class="nav-user{% if active_page == 'profile' %} active{% endif %}" title="{{ current_user.full_name or current_user.username }}">
|
<a href="/profile" class="nav-user{% if active_page == 'profile' %} active{% endif %}" title="{{ current_user.full_name or current_user.username }}">
|
||||||
{% if current_user.avatar %}
|
{% if current_user.avatar %}
|
||||||
@@ -21,6 +24,12 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Full-page clock overlay (click anywhere to dismiss) -->
|
||||||
|
<div id="clock-overlay">
|
||||||
|
<canvas id="swiss-clock-overlay" width="400" height="400"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var btn = document.getElementById('nav-hamburger-btn');
|
var btn = document.getElementById('nav-hamburger-btn');
|
||||||
|
|||||||
@@ -3,11 +3,7 @@
|
|||||||
{% include 'head.html' %}
|
{% include 'head.html' %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body { overflow: hidden; }
|
||||||
margin: 10px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
|
|||||||
@@ -3,15 +3,7 @@
|
|||||||
{% include 'head.html' %}
|
{% include 'head.html' %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body { overflow: visible; }
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 20px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
|
|||||||
@@ -3,19 +3,10 @@
|
|||||||
{% include 'head.html' %}
|
{% include 'head.html' %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body { overflow: visible; }
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 20px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 { color: #333; margin-bottom: 4px; font-size: 1.5em; }
|
h1 { color: #333; margin-bottom: 4px; font-size: 1.5em; }
|
||||||
|
|||||||
Reference in New Issue
Block a user