/* ============================================================================
   Design tokens.

   Palette: Malaysian pewter (cool grey) and batik indigo, with brass reserved
   for exactly one job — marking dates that are not yet officially confirmed.
   Nothing else on the page is warm, so the brass reads as a signal, not decor.

   Every colour used by the app is declared here. Never hardcode a hex in
   markup: theming in Phase 2 is a token swap, not a find-and-replace.
   ========================================================================= */

:root {
  /* Surfaces */
  --c-surface:      #FBFBF9;
  --c-raised:       #FFFFFF;
  --c-sunken:       #F1F1ED;
  /* Half-step, for "a rest day in some of your states but not all". */
  --c-sunken-half:  #F8F8F5;

  /* Text */
  --c-ink:          #16181D;
  --c-ink-2:        #565C6A;
  --c-ink-3:        #868C9A;

  /* Structure */
  --c-line:         #E3E3DE;
  --c-line-strong:  #CBCBC4;

  /* Indigo — primary UI, selection, today */
  --c-accent:       #2B3A67;
  --c-accent-ink:   #FFFFFF;
  --c-accent-soft:  #E5E9F3;

  /* Brass — estimated / unconfirmed dates ONLY */
  --c-brass:        #8A6620;
  --c-brass-soft:   #F5EDDD;

  /* Holiday semantics */
  --c-public:       #B03127;
  --c-public-soft:  #F8E5E2;
  --c-state:        #1B6A87;
  --c-state-soft:   #DFEDF3;
  --c-school:       #2C7549;
  --c-school-soft:  #E0EFE6;

  /* Personal items */
  --c-event:        #6B3FA0;
  --c-event-soft:   #EDE6F6;
  --c-reminder:     #8A6208;
  --c-reminder-soft:#FBF0D9;
  --c-note:         #55606E;
  --c-note-soft:    #ECEEF1;

  --radius:         10px;
  --radius-sm:      6px;

  /* ── Runtime-tunable (js/theme.js writes these on <html>) ──────────────
     Declared here so the app has working defaults before any JS runs and
     so "Reset to default" just means removing the inline overrides. */
  --fs:             1;              /* font scale multiplier */
  --font-ui:        'Archivo', ui-sans-serif, system-ui, 'Segoe UI', sans-serif;
  --font-mono:      'IBM Plex Mono', ui-monospace, 'Cascadia Mono', monospace;
  --cell-min:       76px;           /* month cell height, mobile */
  --cell-min-lg:    104px;          /* month cell height, sm+ */
  --cell-pad:       6px;
  --row-gap:        2px;
}

:root.dark {
  --c-surface:      #131519;
  --c-raised:       #1B1E24;
  --c-sunken:       #0E1014;
  --c-sunken-half:  #16181E;

  --c-ink:          #EAEAE6;
  --c-ink-2:        #9CA2AE;
  --c-ink-3:        #6B7280;

  --c-line:         #282C34;
  --c-line-strong:  #3C424D;

  --c-accent:       #93A6EC;
  --c-accent-ink:   #10131C;
  --c-accent-soft:  #232A42;

  --c-brass:        #D6A855;
  --c-brass-soft:   #2E2718;

  --c-public:       #F28978;
  --c-public-soft:  #3B231F;
  --c-state:        #6BBBD8;
  --c-state-soft:   #14303B;
  --c-school:       #79C99A;
  --c-school-soft:  #163121;

  --c-event:        #B79AE8;
  --c-event-soft:   #262038;
  --c-reminder:     #E0BC63;
  --c-reminder-soft:#2E2716;
  --c-note:         #A2ACBA;
  --c-note-soft:    #212630;
}

/* ── Base ────────────────────────────────────────────────────────────────── */

html {
  -webkit-text-size-adjust: 100%;
  height: 100%;
  height: 100dvh;
}

body.app-shell {
  background: var(--c-surface);
  color: var(--c-ink);
  font-family: var(--font-ui);
  font-variation-settings: 'wdth' 100;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

.app-header {
  padding-top: env(safe-area-inset-top);
}

.app-main {
  padding-bottom: calc(5.75rem + env(safe-area-inset-bottom));
}
@media (min-width: 640px) {
  .app-main { padding-bottom: 2.5rem; }
}

.skip-link {
  position: absolute;
  left: 0.75rem;
  top: -3rem;
  z-index: 100;
  padding: 0.5rem 0.75rem;
  background: var(--c-accent);
  color: var(--c-accent-ink);
  border-radius: var(--radius-sm);
  font-size: 13px;
}
.skip-link:focus {
  top: calc(0.75rem + env(safe-area-inset-top));
}

.hub-only { text-decoration: none; }

/* Installed / standalone: this is the app, not a page on the hub. */
@media (display-mode: standalone), (display-mode: fullscreen) {
  .hub-only { display: none !important; }
}
html.is-standalone .hub-only { display: none !important; }

/* Bottom tab bar — phone only. Safe-area so it sits above the home indicator. */
.tabbar {
  display: none;
}
@media (max-width: 639px) {
  .tabbar {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    gap: 2px;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    background: var(--c-raised);
    border-top: 1px solid var(--c-line);
  }
}
.tabbar-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 44px;
  border-radius: var(--radius-sm);
  font-size: calc(10px * var(--fs));
  letter-spacing: 0.02em;
  color: var(--c-ink-3);
}
.tabbar-item.is-active {
  color: var(--c-accent);
  font-weight: 600;
  background: var(--c-accent-soft);
}
.tabbar-mark {
  font-size: calc(12px * var(--fs));
  font-weight: 600;
  line-height: 1;
}

.toast-bar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(5.25rem + env(safe-area-inset-bottom));
}
@media (min-width: 640px) {
  .toast-bar { bottom: 1.25rem; }
}

/* iOS zooms inputs under 16px. The rest of the UI still uses the --fs scale. */
@media (max-width: 639px) {
  input, select, textarea { font-size: 16px; }
}

[x-cloak] { display: none !important; }

/* Numerals in a grid must align. Tabular figures are functional, not stylistic. */
.tnum {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* The display face, used only for month and year titles. The width axis is
   what gives the page its voice — a normal-width grotesque here would read as
   any dashboard on the internet. */
.display {
  font-family: var(--font-ui);
  font-variation-settings: 'wdth' 118;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: calc(10px * var(--fs));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-ink-3);
}

/* ── Type scale ──────────────────────────────────────────────────────────
   Every size runs through --fs so the font-size setting moves the whole
   interface together. Hardcoded px in markup would silently opt out. */
.fs-8    { font-size: calc(8px    * var(--fs)); }
.fs-9    { font-size: calc(9px    * var(--fs)); }
.fs-10   { font-size: calc(10px   * var(--fs)); }
.fs-10_5 { font-size: calc(10.5px * var(--fs)); }
.fs-11   { font-size: calc(11px   * var(--fs)); }
.fs-11_5 { font-size: calc(11.5px * var(--fs)); }
.fs-12   { font-size: calc(12px   * var(--fs)); }
.fs-12_5 { font-size: calc(12.5px * var(--fs)); }
.fs-13   { font-size: calc(13px   * var(--fs)); }
.fs-13_5 { font-size: calc(13.5px * var(--fs)); }
.fs-14   { font-size: calc(14px   * var(--fs)); }
.fs-15   { font-size: calc(15px   * var(--fs)); }
.fs-17   { font-size: calc(17px   * var(--fs)); }
.fs-20   { font-size: calc(20px   * var(--fs)); }
.fs-24   { font-size: calc(24px   * var(--fs)); }
.fs-34   { font-size: calc(34px   * var(--fs)); }
.fs-46   { font-size: calc(46px   * var(--fs)); }
@media (min-width: 640px) {
  .sm\:fs-46 { font-size: calc(46px * var(--fs)); }
}

/* ── Focus, always visible ───────────────────────────────────────────────── */

:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ── Day cells ───────────────────────────────────────────────────────────── */

/* Density is driven entirely by tokens, so the density setting is a variable
   swap rather than a class permutation on every cell. */
.daycell {
  background: var(--c-raised);
  border: 1px solid transparent;
  transition: background 140ms ease, border-color 140ms ease;
  min-height: var(--cell-min);
  padding: var(--cell-pad);
  gap: var(--row-gap);
}
@media (min-width: 640px) {
  .daycell { min-height: var(--cell-min-lg); }
}
.daycell:hover           { background: var(--c-sunken); }
/* Every selected state rests. */
.daycell.is-rest-all     { background: var(--c-sunken); }
/* Only some do — Selangor works Fridays, Kelantan does not. Shading these
   identically would tell a Selangor user they have a day off that they don't. */
.daycell.is-rest-some    { background: var(--c-sunken-half); }
.daycell.is-outside      { background: var(--c-surface); }
.daycell.is-outside .daynum { color: var(--c-ink-3); }
.daycell.is-selected { border-color: var(--c-accent); background: var(--c-accent-soft); }

.daynum {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  line-height: 1;
  color: var(--c-ink-2);
}

.is-today .daynum {
  background: var(--c-accent);
  color: var(--c-accent-ink);
  border-radius: 999px;
  padding: 4px 6px;
  margin: -4px -6px;
  font-weight: 600;
}

/* ── Indicators ──────────────────────────────────────────────────────────── */

/* Two independent channels, so they never fight:
     COLOUR = what kind of holiday it is
     SHAPE  = how certain we are about it
   Driving both from `currentColor` means a modifier can change the shape
   without ever discarding the type. */
.dot {
  width: 7px; height: 7px;
  border-radius: 999px;
  flex: 0 0 auto;
  background: currentColor;
}
.dot-public   { color: var(--c-public); }
.dot-state    { color: var(--c-state); }
.dot-school   { color: var(--c-school); }
.dot-event    { color: var(--c-event); }
.dot-reminder { color: var(--c-reminder); }
.dot-note     { color: var(--c-note); }

/* Personal items are squares, holidays are circles. A second, redundant
   channel so the two never rely on colour alone to be told apart. */
.dot-event, .dot-reminder, .dot-note { border-radius: 2px; }

/* SIGNATURE — a date whose day is not yet officially fixed is drawn hollow.
   Solid means gazetted. Hollow means "the moon has not been sighted yet".
   The brass dashed rule under the date number carries the same message at
   cell level; brass is reserved for exactly that. */
.dot-estimated {
  background: transparent;
  border: 1.5px solid currentColor;
  width: 8px; height: 8px;
}

/* Partial coverage: applies to some of your selected states, not all.
   Half-filled, so it reads as distinct from hollow at a glance. */
.dot-partial {
  background: linear-gradient(90deg, currentColor 0 50%, transparent 50% 100%);
  border: 1.5px solid currentColor;
  width: 8px; height: 8px;
}

/* The dashed brass rule under an unconfirmed date number. */
.est-rule {
  border-bottom: 1.5px dashed var(--c-brass);
  padding-bottom: 1px;
}

/* ── Chips ───────────────────────────────────────────────────────────────── */

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  line-height: 1.4;
  border: 1px solid transparent;
  white-space: nowrap;
}
.chip-public   { background: var(--c-public-soft);   color: var(--c-public);   border-color: var(--c-public); }
.chip-state    { background: var(--c-state-soft);    color: var(--c-state);    border-color: var(--c-state); }
.chip-school   { background: var(--c-school-soft);   color: var(--c-school);   border-color: var(--c-school); }
.chip-brass    { background: var(--c-brass-soft);    color: var(--c-brass);    border-color: var(--c-brass); }
.chip-event    { background: var(--c-event-soft);    color: var(--c-event);    border-color: var(--c-event); }
.chip-reminder { background: var(--c-reminder-soft); color: var(--c-reminder); border-color: var(--c-reminder); }
.chip-note     { background: var(--c-note-soft);     color: var(--c-note);     border-color: var(--c-note); }

/* A completed reminder stays visible but stops competing for attention. */
.is-done { text-decoration: line-through; opacity: 0.5; }

/* ── Year view heat cells ────────────────────────────────────────────────── */

.ycell {
  aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 9px;
  border-radius: 3px;
  color: var(--c-ink-3);
  cursor: pointer;
  transition: transform 120ms ease;
}
.ycell:hover { transform: scale(1.25); z-index: 5; }
.ycell.y-rest   { background: var(--c-sunken); color: var(--c-ink-3); }
.ycell.y-school { background: var(--c-school-soft); color: var(--c-school); }
.ycell.y-state  { background: var(--c-state-soft);  color: var(--c-state); }
.ycell.y-public { background: var(--c-public-soft); color: var(--c-public); font-weight: 600; }
.ycell.y-today  { outline: 2px solid var(--c-accent); outline-offset: 1px; }

/* A personal item marks the cell without repainting it — the tint is reserved
   for holidays, so an event must not be able to disguise itself as one. */
.ycell.y-item { position: relative; font-weight: 600; }
.ycell.y-item::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 1px;
  width: 4px; height: 2px;
  margin-left: -2px;
  border-radius: 1px;
  background: var(--c-accent);
}

/* ── Heatmap ─────────────────────────────────────────────────────────────
   Cells carry no text on purpose. A heatmap whose squares must also stay
   readable at every tint is a heatmap that cannot use its darkest steps —
   the column header supplies the date instead, and the title attribute the
   detail. The `background` is declared twice so browsers without color-mix
   still get a sensible flat tint. */

.heat {
  aspect-ratio: 1;
  border-radius: 2px;
  background: var(--c-sunken);
  cursor: pointer;
  transition: transform 110ms ease;
}
.heat:hover { transform: scale(1.4); z-index: 5; position: relative; }
.heat-0 { background: var(--c-sunken); }
.heat-1 { background: var(--c-accent-soft); }
.heat-2 { background: var(--c-accent-soft); background: color-mix(in srgb, var(--c-accent) 34%, var(--c-raised)); }
.heat-3 { background: var(--c-accent);      background: color-mix(in srgb, var(--c-accent) 62%, var(--c-raised)); }
.heat-4 { background: var(--c-accent); }
.heat-blank { background: transparent; cursor: default; }
.heat-blank:hover { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .heat:hover { transform: none; }
}

/* ── Week view timed items ───────────────────────────────────────────────── */

.timed {
  display: block;
  width: 100%;
  text-align: left;
  border-left: 2px solid currentColor;
  padding: 1px 3px;
  border-radius: 2px;
  font-size: calc(10px * var(--fs));
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Week view ───────────────────────────────────────────────────────────── */

.hourline { border-top: 1px solid var(--c-line); height: 44px; }
.hourlabel {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 10px; color: var(--c-ink-3);
}

/* ── Motion ──────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .ycell:hover { transform: none; }
}

/* ── Print ────────────────────────────────────────────────────────────────
   Printing is a different medium, not a narrower screen. Ink is expensive and
   tinted backgrounds turn to mud, so colour is carried by a left border and a
   printed label rather than by fills. Everything interactive is removed. */

@page {
  margin: 12mm;
}

@media print {
  .no-print,
  header,
  aside { display: none !important; }

  html, body {
    background: #fff !important;
    color: #000 !important;
    font-size: 10pt;
  }

  /* Undo the theme: printers do not have a dark mode. */
  :root, :root.dark {
    --c-surface: #fff; --c-raised: #fff; --c-sunken: #f2f2f2; --c-sunken-half: #f8f8f8;
    --c-ink: #000; --c-ink-2: #333; --c-ink-3: #555;
    --c-line: #bbb; --c-line-strong: #888;
    --c-accent: #000; --c-accent-ink: #fff; --c-accent-soft: #eee;
    --c-public: #000; --c-state: #000; --c-school: #000;
    --c-event: #000; --c-reminder: #000; --c-note: #000;
    --c-brass: #000;
  }

  main {
    display: block !important;
    max-width: none !important;
    padding: 0 !important;
  }

  section { break-inside: auto; }

  /* Month grid: keep weeks together, never split a row across pages. */
  .daycell {
    border: 0.4pt solid #999 !important;
    min-height: 20mm !important;
    background: #fff !important;
    break-inside: avoid;
  }
  .daycell.is-rest-all { background: #f4f4f4 !important; }
  .daycell.is-outside { color: #999; }
  .is-today .daynum {
    background: #000 !important;
    color: #fff !important;
  }

  /* Colour becomes a rule on the left plus the type letter. */
  .dot {
    width: 0 !important; height: 0 !important;
    border: 0 !important;
    background: none !important;
  }
  .dot::after {
    font-family: var(--font-mono);
    font-size: 7pt;
    letter-spacing: 0;
  }
  .dot-public::after   { content: 'P '; }
  .dot-state::after    { content: 'S '; }
  .dot-school::after   { content: 'H '; }
  .dot-event::after    { content: 'E '; }
  .dot-reminder::after { content: 'R '; }
  .dot-note::after     { content: 'N '; }
  .dot-estimated::after { content: '? '; }

  .chip {
    border: 0.4pt solid #666 !important;
    background: #fff !important;
    color: #000 !important;
  }

  /* Year view: three months per row fits A4 portrait cleanly. */
  section[aria-label="Year view"] {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr) !important;
    gap: 4mm !important;
  }
  section[aria-label="Year view"] > div { break-inside: avoid; }
  .ycell {
    background: #fff !important;
    color: #000 !important;
    border: 0.3pt solid #ddd;
  }
  .ycell.y-public { background: #ddd !important; font-weight: 700; }
  .ycell.y-state  { background: #eee !important; }
  .ycell.y-school { background: #f6f6f6 !important; }
  .ycell.y-today  { outline: 1pt solid #000 !important; }

  /* Week view: the empty hour grid wastes a whole page in print. */
  section[aria-label="Week view"] .hourline { display: none !important; }

  .heat { border: 0.3pt solid #ccc; }

  a[href]::after { content: ''; }   /* no URL footnotes */

  /* A printed calendar with no title is useless once it leaves the printer. */
  .print-title { display: block !important; }
}

.print-title { display: none; }
