/* ==========================================================================
   Clock (/clock/) — self-contained styles, no main.css.

   Everything is driven by attributes on <html> that clock.js keeps in sync:
     data-theme    dark | light | sepia | custom  (resolved; "auto" never lands here)
     data-tone     dark | light                   (custom theme only: how bright its background is)
     data-face     ring | hourglass | mono | analog | celestial
     data-size     s | m | l
     data-seconds  true | false
     data-mode     clock | timer | alarm | stopwatch
     data-perf     full | low                     (Settings > Performance)
   Colours only ever come from the tokens below, so a new theme is one block.
   ========================================================================== */

/* Both fonts carry two sets of vertical metrics that disagree (per 1024
   units: hhea 651 / 393 / 122, OS/2 typo 614 / 410 / 122). Safari reads hhea,
   Chrome and Firefox read typo, so the same CSS put text about .03em higher in
   one than the other. Pinning the hhea numbers here makes them all agree
   (Safari either honours these or already uses the same values), which is what
   lets --font-center below be one number for every browser. */
@font-face {
    font-family: 'HaraldText';
    src: url('/fonts/HaraldReveryTextFont.woff2') format('woff2'),
         url('/fonts/HaraldReveryTextFont.woff') format('woff');
    font-weight: 400;
    font-display: swap;
    ascent-override: 63.57%;
    descent-override: 38.38%;
    line-gap-override: 11.91%;
}
@font-face {
    font-family: 'HaraldMono';
    src: url('/fonts/HaraldReveryMonoFont.woff2') format('woff2'),
         url('/fonts/HaraldReveryMonoFont.woff') format('woff');
    font-weight: 400;
    font-display: swap;
    ascent-override: 63.57%;
    descent-override: 38.38%;
    line-gap-override: 11.91%;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
h2, p, ol { margin: 0; }
[hidden] { display: none !important; }
button, input, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; }
svg { display: block; }

/* ---------- Tokens ---------- */
:root {
    /* Same root size as the main site (theme.css --text-body), so rem means
       the same thing here as everywhere else on haraldrevery.com. */
    font-size: 1.22rem;
    --text-xs: .75rem;           /* smallest UI text; the main site's text-xs */
    --mono: 'HaraldMono', ui-monospace, monospace;
    --text: 'HaraldText', Georgia, serif;
    --ease: cubic-bezier(.25, 1, .5, 1);
    --gutter: clamp(16px, 3vw, 32px);
    --face-scale: 1;
    --bg-opacity: .25;

    /* Optical vertical centring for the site fonts.
       Digits and capitals are .486em tall, stand on the baseline and have
       nothing below it, while the font keeps a deep descent; with the metrics
       pinned above, the baseline sits .126em below the middle of any line box.
       So text is moved down by (--font-center × glyph height − .126em):
         --font-center  which point of a glyph's own height lands on the middle
                        of its box: 0 = baseline, .5 = the glyph's middle,
                        1 = its top. Raise it to lower text, lower it to lift.
       Set it here on :root only; --font-shift is worked out from it once. */
    --font-center: .5;
    --font-cap: .486em;
    --font-baseline: .126em;
    --font-shift: calc(var(--font-center) * var(--font-cap) - var(--font-baseline));
}
:root, :root[data-theme="dark"] {
    color-scheme: dark;
    --bg: #0a0a0a;
    --ink: #f4f2ed;
    --ink-dim: rgba(244, 242, 237, .56);
    --ink-faint: rgba(244, 242, 237, .28);
    --line: rgba(244, 242, 237, .13);
    --line-strong: rgba(244, 242, 237, .26);
    --hover: rgba(244, 242, 237, .07);
    --pressed: rgba(244, 242, 237, .13);
    --panel-bg: rgba(18, 18, 18, .74);
    --alert: #ff6f61;
    --photo-filter: grayscale(.2) brightness(.6) contrast(1.05);
    --scrim: linear-gradient(180deg, rgba(10,10,10,.5) 0%, rgba(10,10,10,.08) 28%, rgba(10,10,10,.08) 72%, rgba(10,10,10,.6) 100%);
}
:root[data-theme="light"] {
    color-scheme: light;
    --bg: #f3f1ec;
    --ink: #161616;
    --ink-dim: rgba(22, 22, 22, .58);
    --ink-faint: rgba(22, 22, 22, .32);
    --line: rgba(22, 22, 22, .13);
    --line-strong: rgba(22, 22, 22, .26);
    --hover: rgba(22, 22, 22, .05);
    --pressed: rgba(22, 22, 22, .1);
    --panel-bg: rgba(247, 245, 240, .8);
    --alert: #c63b2e;
    --photo-filter: grayscale(1) brightness(1.18) contrast(.82);
    --scrim: linear-gradient(180deg, rgba(243,241,236,.6) 0%, rgba(243,241,236,.15) 28%, rgba(243,241,236,.15) 72%, rgba(243,241,236,.65) 100%);
}
:root[data-theme="sepia"] {
    color-scheme: dark;
    --bg: #1b140d;
    --ink: #f1e3c8;
    --ink-dim: rgba(241, 227, 200, .6);
    --ink-faint: rgba(241, 227, 200, .32);
    --line: rgba(241, 227, 200, .15);
    --line-strong: rgba(241, 227, 200, .28);
    --hover: rgba(241, 227, 200, .07);
    --pressed: rgba(241, 227, 200, .13);
    --panel-bg: rgba(30, 22, 14, .8);
    --alert: #ff8a5c;
    --photo-filter: sepia(.7) contrast(1.05) brightness(.58) saturate(1.15) hue-rotate(-8deg);
    --scrim: linear-gradient(180deg, rgba(27,20,13,.5) 0%, rgba(27,20,13,.08) 28%, rgba(27,20,13,.08) 72%, rgba(27,20,13,.62) 100%);
}
/* Custom: the two colours picked in Settings (clock.js sets --custom-bg and
   --custom-ink on <html>); every other shade is mixed from them, in the same
   proportions as the fixed themes. data-tone says whether the background is
   dark or light, which decides the native controls, alert red and photo look. */
:root[data-theme="custom"] {
    --bg: var(--custom-bg, #14181f);
    --ink: var(--custom-ink, #ece6d9);
    --ink-dim: color-mix(in srgb, var(--ink) 57%, transparent);
    --ink-faint: color-mix(in srgb, var(--ink) 30%, transparent);
    --line: color-mix(in srgb, var(--ink) 14%, transparent);
    --line-strong: color-mix(in srgb, var(--ink) 27%, transparent);
    --hover: color-mix(in srgb, var(--ink) 7%, transparent);
    --pressed: color-mix(in srgb, var(--ink) 13%, transparent);
    --panel-bg: color-mix(in srgb, var(--bg) 78%, transparent);
    --scrim: linear-gradient(180deg,
        color-mix(in srgb, var(--bg) 52%, transparent) 0%,
        color-mix(in srgb, var(--bg) 10%, transparent) 28%,
        color-mix(in srgb, var(--bg) 10%, transparent) 72%,
        color-mix(in srgb, var(--bg) 62%, transparent) 100%);
    color-scheme: dark;
    --alert: #ff6f61;
    --photo-filter: grayscale(.2) brightness(.6) contrast(1.05);
}
:root[data-theme="custom"][data-tone="light"] {
    color-scheme: light;
    --alert: #c63b2e;
    --photo-filter: grayscale(1) brightness(1.18) contrast(.82);
}
:root[data-size="s"] { --face-scale: .78; }
:root[data-size="l"] { --face-scale: 1.18; }

body {
    background: var(--bg);
    color: var(--ink);
    font-family: var(--mono);
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
    user-select: none;
    transition: background-color .4s var(--ease), color .4s var(--ease);
}
:focus-visible { outline: 1px solid var(--ink); outline-offset: 3px; }

/* ---------- App shell ---------- */
.app { position: fixed; inset: 0; overflow: hidden; }
.app.idle, .app.idle * { cursor: none !important; }

/* ---------- Background photo (two layers that crossfade) ---------- */
.bg-stage { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.bg-layer {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    filter: var(--photo-filter);
    opacity: 0;
    transition: opacity 1.6s ease;
}
.bg-layer.active { opacity: var(--bg-opacity); }
.bg-scrim { position: absolute; inset: 0; background: var(--scrim); }

/* ==========================================================================
   Controls — one system for the whole app (tools, settings, event card)
     .btn       pill action (data-variant="primary" = filled)
     .chip      small pill (presets, status, sizes)
     .seg-btn   option inside a .seg-group (aria-pressed="true" = chosen)
     .icon-btn  round icon
   Padding is in em so a pill grows with its label. Letter-spaced caps leave
   trailing space after the last glyph, so the left padding carries one extra
   --ls; and the label is moved down by --font-shift (more padding on top, less
   below) so it sits in the optical middle of the pill.
   ========================================================================== */
.btn, .chip, .seg-btn, .mode-btn {
    padding: calc(var(--py) + var(--font-shift)) var(--px) calc(var(--py) - var(--font-shift)) calc(var(--px) + var(--ls));
}
.btn {
    --ls: .24em;
    --py: 1em;
    --px: 1.9em;
    min-width: 9.5rem;
    font-size: .8rem;
    line-height: 1;
    letter-spacing: var(--ls);
    text-transform: uppercase;
    color: var(--ink);
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--panel-bg);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    transition: background-color .2s, border-color .2s, color .2s, opacity .2s, transform .15s;
}
.btn:hover { border-color: var(--ink-dim); background-color: var(--hover); }
.btn:active { transform: scale(.98); }
.btn[data-variant="primary"] { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.btn[data-variant="primary"]:hover { opacity: .88; }
.btn:disabled { opacity: .3; cursor: default; pointer-events: none; }

.chip {
    --ls: .2em;
    --py: .7em;
    --px: 1em;
    font-size: var(--text-xs);
    line-height: 1;
    letter-spacing: var(--ls);
    text-transform: uppercase;
    color: var(--ink-dim);
    border: 1px solid var(--line);
    border-radius: 999px;
    transition: color .2s, border-color .2s, background-color .2s;
}
.chip:hover { color: var(--ink); border-color: var(--line-strong); background: var(--hover); }
.chip[aria-pressed="true"], .chip.active { color: var(--ink); border-color: var(--ink-dim); background: var(--pressed); }

.seg-group {
    display: flex;
    gap: 3px;
    padding: 3px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--hover);
}
/* The settings panel is a size container; on a narrow phone the labels step
   down a little so five of them still fit on one row. */
.seg-btn {
    --ls: .14em;
    --py: .78em;
    --px: .4em;
    flex: 1;
    min-width: 0;
    font-size: clamp(.66rem, 4.1cqi, var(--text-xs));
    line-height: 1;
    letter-spacing: var(--ls);
    text-transform: uppercase;
    text-align: center;
    white-space: nowrap;
    color: var(--ink-dim);
    border-radius: 9px;
    transition: color .2s, background-color .2s;
}
.seg-btn:hover { color: var(--ink); }
.seg-btn[aria-pressed="true"] { color: var(--ink); background: var(--pressed); }

.icon-btn {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    color: var(--ink-dim);
    transition: color .2s, background-color .2s;
}
.icon-btn:hover { color: var(--ink); background: var(--hover); }
.icon-btn svg { width: 18px; height: 18px; }

/* ---------- Top bar ---------- */
.frame-top {
    position: absolute;
    top: 0; left: 0; right: 0;
    z-index: 20;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 12px;
    padding: calc(18px + env(safe-area-inset-top)) var(--gutter) 18px;
    pointer-events: none;
}
.frame-top > * { pointer-events: auto; }
.status { justify-self: start; display: flex; flex-wrap: wrap; gap: 6px; }
.status-chip {
    background: var(--panel-bg);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    color: var(--ink);
    display: inline-flex;
    align-items: center;
    gap: .7em;
}
/* Anything lined up with a label's middle (dots, rules) is moved up by
   --font-shift, because the label itself was moved down by that much. */
.status-chip::before {
    content: "";
    width: 5px; height: 5px;
    border-radius: 50%;
    background: currentColor;
    opacity: .7;
    transform: translateY(calc(-1 * var(--font-shift)));
}
.status-chip.alert { color: var(--alert); border-color: currentColor; }

.modes {
    display: flex;
    gap: 2px;
    padding: 5px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--panel-bg);
    -webkit-backdrop-filter: blur(18px) saturate(1.1);
    backdrop-filter: blur(18px) saturate(1.1);
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.app.chrome-top .modes, .modes:focus-within { opacity: 1; transform: none; }
.mode-btn {
    --ls: .2em;
    --py: .8em;
    --px: 1.1em;
    font-size: var(--text-xs);
    line-height: 1;
    letter-spacing: var(--ls);
    text-transform: uppercase;
    color: var(--ink-dim);
    border-radius: 999px;
    transition: color .2s, background-color .2s;
}
.mode-btn:hover { color: var(--ink); }
.mode-btn[aria-current="true"] { color: var(--ink); background: var(--pressed); }

/* ---------- Stage & views ---------- */
/* The stage is a size container: faces measure themselves against the space
   that is really free (cqw / cqh = 1% of the stage's content box), not the
   whole window. */
.stage {
    position: absolute;
    inset: 0;
    z-index: 5;
    display: grid;
    padding: calc(80px + env(safe-area-inset-top)) var(--gutter) calc(64px + env(safe-area-inset-bottom));
    overflow-y: auto;
    overscroll-behavior: contain;
    container-type: size;
}
.view { display: none; min-width: 0; }
.view.active { display: grid; place-items: center; animation: fade-up .5s var(--ease) both; }
@keyframes fade-up {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* ---------- Shared face pieces ---------- */
/* Every round face is one square of --face-size: the largest square that fits
   beside the face's own text rows (--face-info), times .8, times the size
   setting (S .78 / M 1 / L 1.18, so L fills about 94% and never overflows). */
.face {
    --face-info: 0px;
    --face-size: calc(min(100cqw, 100cqh - var(--face-info)) * .8 * var(--face-scale));
    display: none;
    width: 100%;
    height: 100%;
    min-height: 0;
}
:root[data-face="ring"] .face-ring,
:root[data-face="hourglass"] .face-hourglass,
:root[data-face="mono"] .face-mono,
:root[data-face="analog"] .face-analog,
:root[data-face="celestial"] .face-celestial { display: grid; animation: face-in 1.2s var(--ease) both; }
@keyframes face-in { from { opacity: 0; } to { opacity: 1; } }

/* The site font is not monospaced ('1' is about a third as wide as '0') and
   has no tabular figures, so every digit gets its own fixed slot. See
   digits() in clock.js. */
.digits { display: inline-flex; white-space: nowrap; letter-spacing: 0; }
.digits .dg { display: inline-block; width: 1.04ch; text-align: center; }
.digits .sp { display: inline-block; width: .56ch; text-align: center; opacity: .7; }
.ampm {
    font-size: .2em;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin-left: .6em;
}
.ampm:empty { display: none; }

.time-btn {
    display: inline-flex;
    align-items: baseline;
    line-height: 1;
    color: inherit;
    transition: opacity .2s;
}
.time-btn:hover { opacity: .8; }

.date-link {
    position: relative;
    padding: 4px 2px;
    font-size: clamp(var(--text-xs), 1.5vw, .92rem);
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-dim);
    transition: color .2s;
}
.date-link::after {
    content: "";
    position: absolute;
    left: 50%; bottom: -2px;
    width: 0; height: 1px;
    background: currentColor;
    opacity: .5;
    transform: translateX(-50%);
    transition: width .3s var(--ease);
}
.date-link:hover { color: var(--ink); }
.date-link:hover::after { width: 60%; }
.tz {
    font-size: var(--text-xs);
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.face-info { display: grid; justify-items: center; gap: .5rem; padding-top: 16px; }

/* ---------- Face: Ring ---------- */
.face-ring, .face-hourglass { --face-info: 4rem; grid-template-rows: 1fr auto; justify-items: center; align-items: center; }
.ring-wrap, .hg-wrap { position: relative; width: var(--face-size); aspect-ratio: 1; }
.ring-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.ring-track { fill: none; stroke: currentColor; stroke-width: 1; opacity: .1; }
.ring-arc { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; opacity: .5; }
.tick-minor { stroke: currentColor; stroke-width: .9; opacity: .12; }
.tick-major { stroke: currentColor; stroke-width: 1.8; opacity: .28; }
/* Centred on the ring by its box, then moved by --font-shift so the digits
   themselves (not the box, which has the font's deep descent in it) sit on
   the centre. */
.ring-time {
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, calc(-50% + var(--font-shift)));
    font-size: calc(var(--face-size) * .16);
}
:root[data-seconds="false"] .ring-time { font-size: calc(var(--face-size) * .205); }

/* ---------- Face: Hourglass ---------- */
.hg-svg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    overflow: visible;
    opacity: .52;
    transition: transform .85s var(--ease);
}
.hg-stream { opacity: 0; transition: opacity .2s; }
.hg-stream.flowing { opacity: 1; }
.hg-time {
    position: absolute;
    left: 50%; top: 93%;
    transform: translate(-50%, var(--font-shift));
    font-size: calc(var(--face-size) * .057);
    color: var(--ink-dim);
}

/* ---------- Face: Mono ---------- */
.face-mono { align-content: center; justify-items: center; gap: 36px; }
.mono-time { position: relative; top: var(--font-shift); font-size: calc(clamp(64px, 14vw, 210px) * var(--face-scale)); }
.mono-footer {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 18px;
    width: min(100%, 1000px);
    font-size: clamp(var(--text-xs), 1.5vw, .92rem);
    letter-spacing: .26em;
    text-transform: uppercase;
    color: var(--ink-dim);
}
.mono-footer .center { color: var(--ink); }
.mono-footer .left, .mono-footer .right { display: flex; align-items: center; gap: 14px; }
.mono-footer .right { justify-content: flex-end; }
.mono-footer .left::after,
.mono-footer .right::before {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--line);
    transform: translateY(calc(-1 * var(--font-shift)));
}

/* ---------- Face: Analog ---------- */
.face-analog, .face-celestial { --face-info: 3rem; align-content: center; justify-items: center; gap: 26px; }
.dial-wrap, .orbit-wrap { width: var(--face-size); aspect-ratio: 1; }
.dial-wrap { container-type: inline-size; }
.dial-wrap svg, .orbit-wrap svg { width: 100%; height: 100%; overflow: visible; }
.dial-ring { fill: none; stroke: currentColor; stroke-width: .6; opacity: .7; }
.dial-ring-inner { opacity: .2; }
.dial-wrap .tick-hour { stroke: currentColor; stroke-width: 1.2; stroke-linecap: round; opacity: .85; }
.dial-wrap .tick-min { stroke: currentColor; stroke-width: .5; stroke-linecap: round; opacity: .4; }
/* On a small dial the fine ticks would come out thinner than a pixel. */
@container (max-width: 340px) {
    .dial-wrap .tick-min { stroke-width: .8; }
    .dial-wrap .tick-hour { stroke-width: 1.5; }
}
/* Numerals stand on the alphabetic baseline at (x, y) and are moved down by
   --font-center × their own height, so the chosen point of each digit lands on
   (x, y) whatever the font's ascent and descent say. */
.numeral {
    font-family: var(--mono);
    font-size: 15px;
    fill: currentColor;
    opacity: .78;
    text-anchor: middle;
    transform: translateY(calc(var(--font-center) * var(--font-cap)));
}
.hand-h, .hand-m, .hand-s { stroke: currentColor; stroke-linecap: round; }
.hand-h { stroke-width: 4; }
.hand-m { stroke-width: 2.4; }
.hand-s { stroke-width: 1; opacity: .6; }
.pin-outer { fill: currentColor; }
.pin-inner { fill: var(--bg); }

/* ---------- Face: Celestial ---------- */
/* .cel-sec: the seconds planet's own layer, placed and sized by clock.js
   (buildCelSeconds), turning about the face's centre at its bottom. */
.orbit-wrap { position: relative; }
.orbit-wrap > svg { position: relative; }   /* the dial, over the sky */
.cel-sec { position: absolute; transform-origin: 50% 100%; }

/* The sky behind the dial (clock.js builds it, see "Sky" there): a disc 1.4
   times the face, whose stars fade out towards its rim. Only transform and
   opacity animate in it, which browsers do without repainting. No opacity on
   .sky itself: the browser would then redraw the whole disc as one group on
   every frame of every twinkle. On light backgrounds the stars are the ink
   colour, a little fainter.
   Where there is no room around the face for the whole disc (a phone, size
   L), the face clips it instead of letting it make the page scroll; the cut
   falls where the stars have faded. (hidden: for browsers without clip.) */
.face-celestial { overflow: hidden; overflow: clip; }
.sky { position: absolute; inset: -20%; pointer-events: none; color: var(--ink); }
:root[data-theme="light"] .sky, :root[data-tone="light"] .sky { color: color-mix(in srgb, var(--ink) 60%, transparent); }
.sky-turn { position: absolute; inset: 0; animation: sky-turn 1800s linear infinite; }
@keyframes sky-turn { to { transform: rotate(-360deg); } }
.sky svg { fill: currentColor; }
.tw {
    position: absolute;
    border-radius: 50%;
    background: currentColor;
    box-shadow: 0 0 4px currentColor;
    opacity: .5;
    animation: twinkle 4s ease-in-out infinite alternate;   /* duration and delay: per star */
}
@keyframes twinkle { from { opacity: .08; } to { opacity: .95; } }
/* Under the open event card the sky is blurred anyway; don't make the blur redo itself. */
:root:has(#card-overlay.active) :is(.tw, .sky-turn) { animation-play-state: paused; }
.meteor {
    position: absolute;
    left: 0; top: 0;
    height: 1.5px;
    border-radius: 1px;
    background: linear-gradient(90deg, transparent, currentColor);
    transform-origin: 100% 50%;          /* the head; clock.js moves and turns it */
    opacity: 0;                          /* until its animation starts */
}
.orbit-path { fill: none; stroke: currentColor; stroke-width: .4; stroke-dasharray: .6 1.6; opacity: .32; }
.star { fill: currentColor; }
.planet-h { fill: currentColor; }
.planet-halo { opacity: .15; }
.planet-m { fill: currentColor; opacity: .85; }
.planet-s { fill: currentColor; opacity: .7; }
.core-ring { fill: none; stroke: currentColor; stroke-width: .4; opacity: .55; }
.core-ring-outer { opacity: .25; }
.core-fill { fill: currentColor; opacity: .08; }
.meridian { stroke: currentColor; stroke-width: .3; opacity: .18; }
.cel-meta {
    display: flex;
    align-items: center;
    gap: 18px;
    font-size: clamp(var(--text-xs), 1.5vw, .92rem);
    letter-spacing: .2em;
    color: var(--ink);
}
.cel-meta .time-btn { font-size: 1.1em; }
.cel-meta .ampm { font-size: .7em; margin-left: .5em; }
.dot { width: 4px; height: 4px; border-radius: 50%; background: var(--ink-faint); transform: translateY(calc(-1 * var(--font-shift))); }

:root[data-seconds="false"] .sec-el { display: none; }

/* ==========================================================================
   Tools: timer, alarm, stopwatch
   ========================================================================== */
.tool {
    width: 100%;
    max-width: 1000px;
    display: grid;
    justify-items: center;
    gap: 34px;
    text-align: center;
}
.heading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    width: 100%;
    font-size: var(--text-xs);
    font-weight: 400;
    letter-spacing: .4em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.heading::before, .heading::after {
    content: "";
    flex: 1;
    max-width: 160px;
    height: 1px;
    background: var(--line);
    transform: translateY(calc(-1 * var(--font-shift)));
}

/* The line box is 1em but the digits are only --font-cap tall. Moving the row
   down by --font-shift puts them in the middle of that box, and the negative
   margins then trim the empty space evenly above and below. The inputs take
   the shift back as top padding (cancelled by a negative margin, so the row
   is the same height with inputs or plain digits), which centres their
   hover/focus fill on the digits too. */
.big-numerals {
    position: relative;
    top: var(--font-shift);
    display: inline-flex;
    align-items: baseline;
    justify-content: center;
    margin-block: -.11em;
    font-size: clamp(64px, 13vw, 200px);
    line-height: 1;
    white-space: nowrap;
}
.big-numerals .colon { opacity: .5; padding: 0 .03em; }
.big-numerals .cs { font-size: .2em; color: var(--ink-dim); margin-left: .4em; }

/* An explicit height: Firefox lays text inputs out with line-height: normal
   whatever line-height says, which made the row taller while editing and the
   whole tool jump on Start. */
.num-input {
    width: 2.25ch;
    height: calc(1em + 2 * var(--font-shift));
    padding: calc(2 * var(--font-shift)) 0 0;
    margin-top: calc(-2 * var(--font-shift));
    border: 0;
    border-radius: 14px;
    background: transparent;
    font: inherit;
    line-height: 1;
    letter-spacing: 0;
    text-align: center;
    color: var(--ink);
    caret-color: var(--ink-dim);
    user-select: text;
    transition: background-color .2s;
}
.num-input:hover { background: var(--hover); }
.num-input:focus { outline: none; background: var(--pressed); }
.num-input:disabled { opacity: 1; background: transparent; cursor: default; }

.progress { width: min(520px, 80%); height: 1px; background: var(--line); overflow: hidden; }
.progress span {
    display: block;
    height: 100%;
    background: var(--ink-dim);
    transform-origin: left;
    transform: scaleX(0);
    transition: transform 1s linear;
}

/* Timer face: the clock's ring or hourglass while a countdown runs. clock.js
   copies the clock face's own SVG in here, so the two always look alike. It
   is sized like the clock faces, less the room the heading and buttons take. */
.timer-face {
    --tf-chrome: 9rem;           /* heading + buttons + gaps */
    --tf-size: calc(min(100cqw, 100cqh - var(--tf-chrome)) * .8 * var(--face-scale));
    position: relative;
    width: var(--tf-size);
    aspect-ratio: 1;
    animation: face-in 1.2s var(--ease) both;
}
.timer-face > svg { display: none; }
:root[data-face="ring"] .timer-face > .ring-svg,
:root[data-face="hourglass"] .timer-face > .hg-svg { display: block; }
.tf-time { position: absolute; left: 50%; line-height: 1; }
:root[data-face="ring"] .tf-time {
    top: 50%;
    transform: translate(-50%, calc(-50% + var(--font-shift)));
    font-size: max(var(--text-xs), calc(var(--tf-size) * .16));
}
:root[data-face="hourglass"] .tf-time {
    top: 93%;
    transform: translate(-50%, var(--font-shift));
    font-size: max(var(--text-xs), calc(var(--tf-size) * .075));
    color: var(--ink-dim);
}
.tf-arc-done { visibility: hidden; }   /* a zero-length round-capped dash would still draw a dot */

.presets { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; max-width: 760px; }
.controls { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }

.tool-status {
    min-height: 1.4em;
    font-size: .8rem;
    letter-spacing: .26em;
    text-transform: uppercase;
    color: var(--ink-dim);
}
.tool-status strong { font-weight: 400; color: var(--ink); }
.armed .num-input { color: var(--ink); }
.armed #alarm-edit { opacity: .9; }

/* Stopwatch: an empty first row and the lap log as the last row share the
   leftover height equally, so heading + time + buttons stay put however many
   laps there are; the log scrolls inside its own row. `contain: size` is what
   makes this work — without it the list's full height feeds back into the
   tool's height and both "equal" rows grow with every lap. */
.view[data-view="stopwatch"].active { align-items: stretch; }
.tool-sw { grid-template-rows: 1fr auto auto auto minmax(120px, 1fr); }
.tool-sw::before { content: ""; }
.sw-log {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    align-content: start;
    width: min(560px, 100%);
    contain: size;
    container-type: inline-size;         /* see the lap rows below */
}
.sw-log-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--line);
    font-size: var(--text-xs);
    letter-spacing: .26em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.laps {
    min-height: 0;
    overflow-y: auto;
    padding: 0;
    list-style: none;
    font-size: .8rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--ink-dim);
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
}
/* Row: "Lap 03" · note · the lap's own time · total. The note is a plain
   text field that reads as text until it is hovered or focused. The times
   get fixed, right-aligned columns: every row is its own grid, and the
   font's narrow '1' would otherwise make them line up differently per row. */
.laps li {
    display: grid;
    grid-template-columns: 4.6em minmax(0, 1fr) 6.4em 6.4em;
    align-items: baseline;
    gap: 14px;
    padding: 10px 2px;
    border-bottom: 1px solid var(--line);
    text-align: left;
}
.laps .idx { color: var(--ink-faint); }
.laps .lap { color: var(--ink); }
.laps .lap, .laps .total { text-align: right; white-space: nowrap; }
.laps .note {
    min-width: 0;
    width: 100%;
    margin: -5px 0;
    padding: 5px 8px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font: inherit;
    letter-spacing: .04em;
    text-transform: none;
    text-overflow: ellipsis;
    color: var(--ink);
    caret-color: var(--ink-dim);
    user-select: text;
    transition: background-color .2s;
}
.laps .note:hover { background: var(--hover); }
.laps .note:focus { outline: none; background: var(--pressed); }
.laps .note::placeholder { color: var(--ink-faint); opacity: 1; }
/* With a mouse the "Add a note" hint only shows on the row you point at;
   touch screens keep it, as there is nothing to hover. */
@media (hover: hover) {
    .laps .note::placeholder { opacity: 0; }
    .laps li:hover .note::placeholder, .laps .note:focus::placeholder { opacity: 1; }
}
/* Too narrow for four columns: the note gets a line of its own. */
@container (max-width: 460px) {
    .laps li { grid-template-columns: 1fr 6.4em 6.4em; grid-template-areas: "idx lap total" "note note note"; row-gap: 8px; }
    .laps .idx { grid-area: idx; }
    .laps .lap { grid-area: lap; }
    .laps .total { grid-area: total; }
    .laps .note { grid-area: note; margin: 0; }
}

/* ---------- Ringing ---------- */
.ring-banner {
    position: fixed;
    left: 50%;
    bottom: calc(44px + env(safe-area-inset-bottom));
    z-index: 250;                    /* above the event card overlay (200) */
    display: flex;
    align-items: center;
    gap: 18px;
    max-width: calc(100vw - 32px);
    padding: 8px 8px 8px 24px;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--panel-bg);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    transform: translateX(-50%);
    animation: banner-in .45s var(--ease) both;
}
@keyframes banner-in {
    from { opacity: 0; transform: translate(-50%, 12px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}
.ring-label {
    position: relative;
    top: var(--font-shift);
    font-size: .78rem;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: var(--alert);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ring-banner .btn { min-width: 0; }
.app.ringing .ring-label,
.app.ringing-timer #timer-display,
.app.ringing-timer .tf-time,
.app.ringing-alarm #alarm-edit,
.app.ringing .face .time-btn {
    color: var(--alert);
    animation: pulse 1.2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .45; } }

/* ---------- Site link (bottom left) ---------- */
.site-link {
    position: fixed;
    left: var(--gutter);
    bottom: calc(18px + env(safe-area-inset-bottom));
    z-index: 30;
    font-family: var(--text);
    font-size: .95rem;
    color: var(--ink-dim);
    text-decoration: none;
    transition: opacity .4s, color .2s;
}
.site-link:hover { color: var(--ink); }

/* ---------- Settings ---------- */
.settings-trigger {
    position: fixed;
    right: 0; bottom: 0;
    z-index: 30;
    display: grid;
    place-items: end;
    width: 140px;
    height: 140px;
    padding: 0 var(--gutter) calc(16px + env(safe-area-inset-bottom)) 0;
}
/* Just the icon: no border or disc behind it, not even on hover. */
.cog {
    width: 40px;
    height: 40px;
    color: var(--ink);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.cog:hover { background: none; }
.cog svg { transition: transform .6s var(--ease); }
.cog:hover svg, .cog[aria-expanded="true"] svg { transform: rotate(60deg); }
.settings-trigger:hover .cog, .cog:focus-visible, .cog[aria-expanded="true"] { opacity: 1; transform: none; }

@media (hover: hover) {
    .site-link { opacity: 0; }
    .site-link:hover, .site-link:focus-visible { opacity: 1; }
    .site-link::before { content: ""; position: absolute; inset: -2.5rem; }
}
/* Touch screens have no hover to reveal the chrome, so keep it visible. */
@media (hover: none) {
    .modes, .cog { opacity: 1; transform: none; }
}

.settings-panel {
    position: fixed;
    right: var(--gutter);
    bottom: calc(68px + env(safe-area-inset-bottom));
    z-index: 40;
    container-type: inline-size;         /* see .seg-btn and .face-grid */
    width: 400px;
    max-width: calc(100vw - 2 * var(--gutter));
    max-height: calc(100dvh - 84px - env(safe-area-inset-bottom));
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
    padding: 14px 20px 18px;
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 20px;
    /* More opaque than the other glass surfaces: the panel sits over the
       clock face, which would otherwise read through the labels. */
    background: color-mix(in srgb, var(--bg) 90%, transparent);
    -webkit-backdrop-filter: blur(28px) saturate(1.1);
    backdrop-filter: blur(28px) saturate(1.1);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .25);
    opacity: 0;
    transform: translateY(12px) scale(.98);
    transform-origin: bottom right;
    pointer-events: none;
    transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.settings-panel.open { opacity: 1; transform: none; pointer-events: auto; }
.panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin: 0 -8px 6px 0;
    font-size: var(--text-xs);
    letter-spacing: .32em;
    text-transform: uppercase;
    color: var(--ink-dim);
}
.panel-head > span { position: relative; top: var(--font-shift); }
/* minmax(0, …): a column never grows past the panel for a wide child (the
   range input's default width, for one); the child shrinks instead. */
.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 14px 0; border-top: 1px solid var(--line); }
.panel-head + .field { border-top: 0; padding-top: 4px; }
.field-pair { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
.field-pair > div { display: grid; gap: 10px; align-content: start; }
.field-label, .sub-label {
    font-size: var(--text-xs);
    letter-spacing: .3em;
    text-transform: uppercase;
    color: var(--ink-dim);
}

.face-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.face-thumb {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: 7px;
    aspect-ratio: .9;
    padding: 4px 2px;
    border: 1px solid var(--line);
    border-radius: 10px;
    font-size: .7rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-dim);
    transition: color .2s, border-color .2s, background-color .2s;
}
.face-thumb svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.3; stroke-linecap: round; }
.face-thumb svg .fill { fill: currentColor; stroke: none; }
.face-thumb .thumb-text { font-size: .72rem; letter-spacing: 0; line-height: 24px; }
.face-thumb:hover { color: var(--ink); border-color: var(--line-strong); }
.face-thumb[aria-pressed="true"] { color: var(--ink); border-color: var(--ink-dim); background: var(--pressed); }
/* Five captions no longer fit on a phone-wide panel: three per row instead. */
@container (max-width: 350px) {
    .face-grid { grid-template-columns: repeat(3, 1fr); }
    .face-thumb { aspect-ratio: auto; padding: 12px 2px; font-size: var(--text-xs); }
}

.photo-strip { display: grid; grid-template-columns: repeat(auto-fill, minmax(36px, 1fr)); gap: 5px; }
.photo-thumb {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 1;
    border-radius: 7px;
    background-size: cover;
    background-position: center;
    border: 1px solid transparent;
    transition: transform .2s var(--ease), border-color .2s;
}
.photo-thumb:hover { transform: translateY(-2px); }
.photo-thumb[aria-pressed="true"] { border-color: var(--ink); box-shadow: inset 0 0 0 2px var(--bg); }
.photo-thumb.add {
    display: grid;
    place-items: center;
    border: 1px dashed var(--line-strong);
    color: var(--ink-dim);
    font-size: 1.2rem;
    line-height: 1;
}
.photo-thumb.add:hover { color: var(--ink); border-color: var(--ink-dim); transform: none; }
.photo-remove {
    position: absolute;
    top: -6px; right: -6px;
    display: none;
    place-items: center;
    width: 18px; height: 18px;
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    background: var(--bg);
    color: var(--ink);
    font-size: 10px;
    line-height: 1;
}
.photo-item { position: relative; }
.photo-item:hover .photo-remove, .photo-remove:focus-visible { display: grid; }
@media (hover: none) { .photo-remove { display: grid; } }

.slider-row { display: flex; align-items: center; gap: 12px; }
.slider-row input[type="range"] {
    flex: 1;
    min-width: 0;                /* its default width is in em, too wide on a phone */
    height: 2px;
    margin: 0;
    -webkit-appearance: none;
    appearance: none;
    background: var(--line-strong);
    border-radius: 2px;
    cursor: pointer;
}
.slider-row input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 14px; height: 14px;
    border-radius: 50%;
    background: var(--ink);
    border: 2px solid var(--bg);
    box-shadow: 0 0 0 1px var(--ink-dim);
}
.slider-row input[type="range"]::-moz-range-thumb {
    width: 14px; height: 14px;
    border-radius: 50%;
    background: var(--ink);
    border: 2px solid var(--bg);
    box-shadow: 0 0 0 1px var(--ink-dim);
}
.slider-row .val { min-width: 4ch; font-size: var(--text-xs); letter-spacing: .1em; text-align: right; color: var(--ink-dim); }
.slider-row .sub-label, .slider-row .val { position: relative; top: var(--font-shift); }

/* Custom theme colours: two round swatches, shown while "Custom" is chosen.
   The native colour input sits inside its label, so the whole pill opens it. */
.custom-colors { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.swatch {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    padding: 5px 12px 5px 5px;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: var(--text-xs);
    letter-spacing: .18em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--ink-dim);
    cursor: pointer;
    transition: color .2s, border-color .2s;
}
.swatch:hover { color: var(--ink); border-color: var(--line-strong); }
.swatch span { position: relative; top: var(--font-shift); overflow: hidden; text-overflow: ellipsis; }
.swatch input[type="color"] {
    flex: none;
    width: 28px; height: 28px;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    background: none;
    cursor: pointer;
}
.swatch input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.swatch input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 50%; }
.swatch input[type="color"]::-moz-color-swatch { border: 0; border-radius: 50%; }
.contrast-note { font-size: var(--text-xs); letter-spacing: .1em; color: var(--alert); }

/* ---------- Responsive ---------- */
@media (max-width: 760px) {
    .frame-top { grid-template-columns: 1fr; justify-items: center; }
    .status { justify-self: center; order: 2; }
    .frame-top > div:last-child { display: none; }
    .mode-btn { --ls: .14em; --py: .72em; --px: .8em; font-size: .66rem; }
    .stage { padding-top: calc(120px + env(safe-area-inset-top)); }
    .btn { --py: .9em; min-width: 8rem; }
}
@media (max-width: 760px) and (hover: none) {
    .site-link { display: none; }
}
@media (max-height: 640px) {
    .stage { padding-top: calc(76px + env(safe-area-inset-top)); padding-bottom: 20px; }
    .tool { gap: 20px; }
    .big-numerals { font-size: clamp(56px, 11vw, 150px); }
    /* Too short for heading + face + buttons: the face drops the heading. */
    .tool:has(> .timer-face:not([hidden])) .heading { display: none; }
    .timer-face { --tf-chrome: 4.5rem; }
}

/* ---------- Performance: Low (Settings) ----------
   For older phones and computers, and the battery. clock.js stops the smooth
   motion and the shooting stars; here the sky stops turning and twinkling,
   and the glass surfaces lose their blur (a blur is worked out again
   whenever what lies behind it changes) and turn nearly opaque instead. */
:root[data-perf="low"] { --panel-bg: color-mix(in srgb, var(--bg) 92%, transparent); }
:root[data-perf="low"] :is(.btn, .status-chip, .modes, .ring-banner, .settings-panel, .card-overlay) {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}
:root[data-perf="low"] :is(.tw, .sky-turn) { animation: none; }

@media (prefers-reduced-motion: reduce) {
    .view.active, .face { animation: none !important; }
    .hg-svg { transition: none; }
    .app.ringing .face .time-btn, .app.ringing-timer #timer-display, .app.ringing-timer .tf-time, .app.ringing-alarm #alarm-edit { animation: none; }
    .timer-face { animation: none; }
    .sky-turn, .tw { animation: none !important; }
}
