:root{
  --bg:#000;
  --fg:#fff;
  --gap:10px;
  --pad:clamp(18px,3.4vw,56px);
  --cols:12;
  --wx:2;
  --wxRow:2;
  --stat:2;
  --statRow:2;
}

/* ---------- fonts ----------
   Selawik is Microsoft's open-source (SIL OFL 1.1) metric-compatible stand-in
   for Segoe UI. Segoe itself is proprietary and can't be self-hosted, so it
   stays first in the stack: Windows visitors match it locally and never fetch
   anything. Everyone else falls through to these. Only the three weights the
   CSS actually uses are shipped.
   The paths are relative to THIS file, in assets/, not to index.php — hence
   the ../ back out to public/fonts/. */
@font-face{
  font-family:"Selawik";
  src:local("Selawik Light"),url("../fonts/selawkl.woff2") format("woff2");
  font-weight:300;
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:"Selawik";
  src:local("Selawik"),url("../fonts/selawk.woff2") format("woff2");
  font-weight:400;
  font-style:normal;
  font-display:swap;
}
@font-face{
  font-family:"Selawik";
  src:local("Selawik Semibold"),url("../fonts/selawksb.woff2") format("woff2");
  font-weight:600;
  font-style:normal;
  font-display:swap;
}

*,*::before,*::after{box-sizing:border-box}
html{background:var(--bg)}
body{
  margin:0;
  padding:var(--pad);
  background:var(--bg);
  color:var(--fg);
  font-family:"Segoe UI","Selawik",-apple-system,BlinkMacSystemFont,
              "Helvetica Neue",Roboto,Arial,sans-serif;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

/* ---------- section labels ---------- */
.sect{
  margin:clamp(22px,2.6vw,40px) 0 clamp(8px,.9vw,14px);
  font-size:clamp(13px,1.2vw,16px);
  font-weight:300;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:rgba(255,255,255,.4);
}

/* ---------- tile grid ---------- */
.wrap{container-type:inline-size}
.tiles{
  display:grid;
  grid-template-columns:repeat(var(--cols),minmax(0,1fr));
  gap:var(--gap);
  grid-auto-flow:row dense;
  /* fallback for browsers without container query units */
  grid-auto-rows:calc((100vw - (2 * var(--pad)) - ((var(--cols) - 1) * var(--gap))) / var(--cols));
  grid-auto-rows:calc((100cqw - ((var(--cols) - 1) * var(--gap))) / var(--cols));
}

/* Two grids stacked under one heading read as one strip with a seam in it,
   so the seam is plainly wider than the gap between tiles but nothing like
   the space a heading of its own would take. */
.tiles + .tiles{margin-top:calc(var(--gap) * 2)}

.t--xs{grid-column:span 1;grid-row:span 1}
.t--sm{grid-column:span 2;grid-row:span 1}
.t--md{grid-column:span 2;grid-row:span 2}
.t--lg{grid-column:span 4;grid-row:span 2}
.t--hero{grid-column:span var(--hero);grid-row:span 2}
.t--stat{grid-column:span var(--stat);grid-row:span var(--statRow)}
.t--wx{grid-column:span var(--wx);grid-row:span var(--wxRow)}

/* ---------- the tile itself ---------- */
.tile{
  position:relative;
  display:block;
  overflow:hidden;
  min-width:0;
  background:var(--tile,#1f1f1f);
  color:#fff;
  text-decoration:none;
  border:0;
  border-radius:0;
  transform-style:preserve-3d;
  transition:transform .12s cubic-bezier(.2,.7,.3,1),filter .18s ease;
  will-change:transform;
}
.tile:focus-visible{outline:3px solid #fff;outline-offset:-3px}
a.tile:hover{filter:brightness(1.14)}
a.tile:active{transform:scale(.955) perspective(700px) rotateX(3deg)}

/* tile artwork: the letter watermark is always there, the image sits on top */
.tile__icon{
  position:absolute;
  top:clamp(9px,1vw,16px);
  left:clamp(9px,1vw,16px);
  width:var(--art,38px);
  height:var(--art,38px);
  object-fit:contain;
  object-position:left top;
  pointer-events:none;
  user-select:none;
}
.t--lg .tile__icon{--art:clamp(46px,4.6vw,70px)}
.t--md .tile__icon{--art:clamp(38px,3.8vw,56px)}
.t--sm .tile__icon{--art:clamp(28px,2.6vw,38px)}
.t--xs .tile__icon{--art:clamp(26px,2.4vw,34px)}

/* big watermark letter */
.tile__mono{
  position:absolute;
  right:.06em;
  top:50%;
  transform:translateY(-54%);
  font-size:var(--mono,84px);
  font-weight:200;
  line-height:.8;
  color:rgba(255,255,255,.08);
  pointer-events:none;
  user-select:none;
}
.t--lg .tile__mono{--mono:clamp(80px,9.5vw,150px)}
.t--md .tile__mono{--mono:clamp(64px,7.4vw,112px)}
.t--sm .tile__mono{--mono:clamp(44px,4.6vw,70px)}
.t--xs .tile__mono{--mono:clamp(34px,3.8vw,54px)}

/* faces (front = name, back = sub) */
.tile__faces{position:absolute;inset:0;height:200%}
.tile__face{
  position:relative;
  height:50%;
  padding:clamp(9px,1vw,16px);
  display:flex;
  flex-direction:column;
  justify-content:flex-end;
}
.tile__name{
  font-size:clamp(12px,1.15vw,17px);
  font-weight:400;
  letter-spacing:.005em;
  line-height:1.2;
  overflow-wrap:anywhere;
}
.t--lg .tile__name{font-size:clamp(16px,1.6vw,23px)}
.t--xs .tile__name{font-size:clamp(10px,.95vw,13px);letter-spacing:0}
.tile__sub{
  font-size:clamp(13px,1.35vw,20px);
  font-weight:200;
  line-height:1.25;
  color:rgba(255,255,255,.92);
}

@keyframes metroFlip{
  0%,44%   {transform:translateY(0)}
  52%,94%  {transform:translateY(-50%)}
  100%     {transform:translateY(0)}
}
.tile--live .tile__faces{animation:metroFlip 16s cubic-bezier(.6,0,.2,1) infinite}
.tile--live:nth-of-type(2n) .tile__faces{animation-delay:-3.5s}
.tile--live:nth-of-type(3n) .tile__faces{animation-delay:-7s}
.tile--live:nth-of-type(5n) .tile__faces{animation-delay:-11s}
.tile--live:hover .tile__faces{animation-play-state:paused}

/* ---------- hero / stat tiles ---------- */
.tile--plain{--tp:clamp(12px,1.3vw,22px);padding:var(--tp);display:flex;flex-direction:column}
.tile--hero{background:var(--a-cobalt)}
.tile--ip{background:#2b2b2b;border:2px solid rgba(255,255,255,.16)}
.tile--loc{background:var(--a-teal)}
.k{
  font-size:clamp(10px,.85vw,12px);
  font-weight:400;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:rgba(255,255,255,.62);
}
.hero__time{
  margin-top:auto;
  font-size:clamp(46px,7.4vw,104px);
  font-weight:200;
  line-height:.9;
  letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
}
.hero__date{
  margin-top:.35em;
  font-size:clamp(12px,1.25vw,19px);
  font-weight:300;
  color:rgba(255,255,255,.85);
}
.stat__val{
  margin-top:auto;
  font-size:clamp(17px,1.85vw,27px);
  font-weight:200;
  line-height:1.1;
  overflow-wrap:anywhere;
  font-variant-numeric:tabular-nums;
}
.stat__val small{display:block;font-size:.62em;color:rgba(255,255,255,.7);margin-top:.4em}

/* ---------- weather tile ----------
   Filled in by the browser after paint (see assets/weather.js); everything
   below is also what the placeholder looks like. The tile
   is laid out as a column of [now][tomorrow] on square shapes and flipped to
   a row on the short, wide ones the narrow breakpoints give it.

   Colour is two custom properties the script sets from the conditions:
   --wxA for now, --wxB for tomorrow. The gradient holds --wxA flat for most
   of the tile, turns over between --wxS1 and --wxS2, and is solid --wxB from
   there on — so the fade lands on the divider and the tomorrow row sits on a
   flat colour of its own rather than in the middle of a blend. Registering
   the two colours with @property is what lets them animate: a gradient can't
   be transitioned, but the values it is built from can. */
@property --wxA{syntax:"<color>";inherits:true;initial-value:#3d4348}
@property --wxB{syntax:"<color>";inherits:true;initial-value:#3d4348}

.tile--wx{
  /* The three clamps the bottom of the tile is built from. They are used
     both here and by the rules below, so the gradient can be positioned
     against the layout rather than against a guess at it. */
  --wxGap:clamp(6px,.7vw,11px);      /* between the two halves of the body */
  --wxRowPad:clamp(5px,.6vw,9px);    /* above the tomorrow row's content */
  --wxIcoSm:clamp(21px,2vw,29px);    /* the tomorrow glyph */

  /* Where tomorrow's colour becomes solid, measured up from the bottom
     edge: the top of the tomorrow glyph. Derived rather than typed, because
     the tile is 149px tall at one breakpoint and 202px at another — a fixed
     length, or a percentage, is only ever right at one of them. It works out
     at 22-25% of the tile at every size, and landing it exactly on the glyph
     is what lets that glyph knock out against a flat colour. */
  --wxIcoTop:calc(var(--tp) + var(--wxIcoSm));
  --wxDiv:calc(var(--wxIcoTop) + var(--wxRowPad) + 1px);
  --wxDescBot:calc(var(--wxDiv) + var(--wxGap));

  /* stacked: today on top, tomorrow along the bottom. The fade runs from the
     bottom of today's description to the top of the tomorrow glyph — the
     whole of the gap between them, which is all the room there is without
     tinting the description. */
  --wxDir:180deg;
  --wxS1:calc(100% - var(--wxDescBot));
  --wxS2:calc(100% - var(--wxIcoTop));
  --tile:var(--wxA);
  background:linear-gradient(var(--wxDir),
             var(--wxA) 0 var(--wxS1),
             var(--wxB) var(--wxS2));
  /* the two colours, and then .tile's own transform and filter again: this
     shorthand replaces that one outright, and a tile that is a link still
     has to press when clicked and brighten under the pointer */
  transition:--wxA .5s ease,--wxB .5s ease,
             transform .12s cubic-bezier(.2,.7,.3,1),filter .18s ease;
}
/* the tomorrow row sits on --wxB, so anything in it that has to knock out
   what it overlaps (the cloud in a partly-cloudy glyph) matches that end of
   the gradient rather than the top of it */
.wx__body{
  margin-top:auto;
  display:flex;
  flex-direction:column;
  gap:var(--wxGap);
  min-width:0;
}
.wx__now{display:flex;align-items:center;gap:clamp(7px,.75vw,11px);min-width:0}
.wx__nowt{display:flex;flex-direction:column;min-width:0}
.wx__ico{
  flex:0 0 auto;
  width:var(--wxi,clamp(32px,3.1vw,46px));
  height:var(--wxi,clamp(32px,3.1vw,46px));
  fill:none;
  stroke:currentColor;
  stroke-width:1.7;
  stroke-linecap:round;
  stroke-linejoin:round;
  overflow:visible;
}
.wx__ico--sm{--wxi:var(--wxIcoSm)}
.wx__temp{
  font-size:clamp(29px,3.2vw,48px);
  font-weight:200;
  line-height:.9;
  letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
}
.wx__desc{
  margin-top:.3em;
  font-size:clamp(11px,1.05vw,15px);
  font-weight:300;
  line-height:1.2;
  color:rgba(255,255,255,.85);
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.wx__tmw{
  /* the tomorrow row sits on --wxB, so anything in it that has to knock out
     what it overlaps (the cloud in a partly-cloudy glyph) matches that end
     of the gradient rather than the top of it */
  --tile:var(--wxB);
  display:flex;
  align-items:center;
  gap:.5em;
  min-width:0;
  padding-top:var(--wxRowPad);
  border-top:1px solid rgba(255,255,255,.22);
  font-size:clamp(11px,1.05vw,15px);
  font-weight:300;
  font-variant-numeric:tabular-nums;
}
.wx__tmwk{
  font-size:.78em;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:rgba(255,255,255,.62);
}
.wx__hi{font-weight:400}
.wx__lo{color:rgba(255,255,255,.68)}
.wx__tmw .wx__ico{margin-left:auto}
.wx--err .wx__body{opacity:.6}

/* ---------- world clocks: ALWAYS exactly one row, never scrolls ----------
   --tzc (column count) and the promote/group rules are NOT here: they are
   generated from the $timezones array by the layout solver in index.php, and
   land in the inline <style> that follows this file. The base state below is
   what they diff against — everything grouped, no city tiles.
   Every font-size here is clamped with a floor it reaches well before the
   narrowest stage, so below desktop widths the text is a fixed, legible size.
   A row that runs out of room loses a card rather than shrinking its type. */
.tz{
  display:grid;
  grid-template-columns:repeat(var(--tzc),minmax(0,1fr));
  gap:var(--gap);
}
.tz__item{
  container-type:inline-size;
  min-width:0;
  min-height:var(--tzH);
  padding:clamp(8px,.85vw,13px);
  background:#161616;
  border-top:3px solid var(--tzk,var(--a-cyan));
  display:flex;
  flex-direction:column;
  overflow:hidden;
}
.tz__city{
  display:flex;align-items:center;gap:5px;
  font-size:clamp(12px,.92vw,13px);
  font-weight:300;
  letter-spacing:.02em;
  min-width:0;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.tz__cc{
  flex:0 0 auto;
  font-size:9px;
  font-weight:600;
  letter-spacing:.06em;
  padding:1px 4px;
  background:rgba(255,255,255,.14);
  color:rgba(255,255,255,.8);
}
.tz__clock{
  margin-top:auto;
  display:flex;align-items:baseline;gap:5px;
  font-size:clamp(22px,2.05vw,30px);
  font-weight:200;
  line-height:1;
  letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
  min-width:0;
}
.tz__off{
  flex:0 1 auto;
  font-size:.42em;font-weight:400;
  color:rgba(255,255,255,.5);letter-spacing:.03em;
  overflow:hidden;
}

/* ---- group tile: region label + one compact line per city ---- */
.tz__region{
  font-size:clamp(10px,.8vw,11px);
  font-weight:400;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:rgba(255,255,255,.5);
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.tz__rows{margin-top:auto;display:flex;flex-direction:column;gap:2px;min-width:0}
.tz-row{
  align-items:baseline;
  justify-content:space-between;
  gap:5px;
  font-size:clamp(12px,1vw,13px);
  font-weight:300;
  line-height:1.3;
  white-space:nowrap;
  min-width:0;
  font-variant-numeric:tabular-nums;
}
.tz-row b{
  flex:0 0 auto;
  font-weight:600;
  font-size:.85em;
  letter-spacing:.06em;
  color:rgba(255,255,255,.55);
  cursor:help;
}
/* time and offset travel together on the right of the row */
.tz-row__t{display:flex;align-items:baseline;gap:4px;min-width:0}
.tz-row .tz__time{font-weight:200;font-size:1.15em;letter-spacing:-.01em}
.tz__goff{
  flex:0 0 auto;
  font-size:.72em;
  font-weight:400;
  color:rgba(255,255,255,.45);
  letter-spacing:.02em;
  font-variant-numeric:tabular-nums;
}
/* The row can't fall below its fixed type size, so on a card too narrow to
   hold 'LON 09:14 +0100' the offset steps aside rather than being clipped.
   A container query, so it tracks the card rather than the viewport; the
   width is the card's content box, and only the three region tiles on a
   phone under ~370px ever get this small. */
@container (max-width:88px){
  .tz__goff{display:none}
}

/* base state: everything grouped */
.tz-city{display:none}
.tz-grp{display:flex}
.tz-row{display:flex}

/* ---------- footer ---------- */
.foot{
  margin-top:clamp(28px,3.4vw,52px);
  padding-top:14px;
  border-top:1px solid rgba(255,255,255,.1);
  font-size:12px;
  font-weight:300;
  letter-spacing:.05em;
  color:rgba(255,255,255,.35);
}


/* one-row weather tile: now and tomorrow side by side instead of stacked */
@media (max-width:659px){
  /* Tomorrow is to the right now. Same idea as above, turned 90 degrees:
     the fade starts at the divider and is done by the time it reaches the
     divider. Giving the block a min-width is what makes that expressible —
     its content is "TMRW 00° 00°" and a glyph, so it settles around 115px on
     its own, and pinning it takes the last guess out of the gradient. The
     now block shrinks instead (its description ellipsises), so the two can
     never overlap however long the conditions read. */
  .tile--wx{
    --wxTw:120px;
    --wxRowPadL:clamp(12px,2.6vw,20px);
    --wxDir:90deg;
    --wxS1:calc(100% - var(--wxTw) - var(--tp) - 40px);
    --wxS2:calc(100% - var(--wxTw) - var(--tp));
  }
  .wx__body{flex-direction:row;align-items:center;gap:var(--wxRowPadL)}
  .wx__now{flex:0 1 auto;min-width:0}
  .wx__tmw{
    flex:0 0 auto;
    min-width:var(--wxTw);
    margin-left:auto;
    padding-top:0;
    padding-left:var(--wxRowPadL);
    border-top:0;
    border-left:1px solid rgba(255,255,255,.22);
    align-self:stretch;
  }
  .wx__tmw .wx__ico{margin-left:0}
}

@media (prefers-reduced-motion:reduce){
  .tile--live .tile__faces{animation:none}
  .tile{transition:none}
}
