/* Contact Sheet theme. Colors and type live in the variables below. */
@import url("https://fonts.googleapis.com/css2?family=Archivo:wght@500;800&family=IBM+Plex+Mono:wght@400;500;600&display=swap");

/* Colour palette: change the file name to switch (options in palettes/: mist, seaglass, sage,
   sky, blush, silver, ink). The palette sets every colour; this file only uses them. */
@import url("palettes/mist.css");

:root {
  --font-display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --font-body: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --max: 1200px;
  --gap: 24px;
  --page-top: 28px; /* header rule → first content, every public page (G-12) */
  color-scheme: light;
}

/* Dark mode: chosen with the toggle (assets/theme.js sets data-theme="dark"). Light is the default.
   Its colours come from the palette file. */
:root[data-theme="dark"] { color-scheme: dark; }

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 15px/1.6 var(--font-body);
}
a { color: var(--fg); }
a:hover { color: var(--strong); }
img { display: block; max-width: 100%; }
h1, h2 { font-family: var(--font-display); font-weight: 800; margin: 0; line-height: 1.05; }
h1 { font-size: clamp(36px, 6vw, 56px); }
h2 { font-size: clamp(28px, 4vw, 40px); }
p { margin: 0; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Read by screen readers and search engines, not shown (e.g. the home page's name heading). */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.skip { position: absolute; left: -9999px; top: 8px; background: var(--accent); color: var(--on-accent); padding: 8px 12px; z-index: 10; }
.skip:focus { left: 8px; }
/* Space between the header and the first content on every public page, current and future (G-12).
   Postcard pages have their own <main class="pc-main"> and are not affected. */
main#content { padding-top: var(--page-top); }

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 var(--gap); }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.accent { color: var(--accent); }
.label { font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.label.accent { color: var(--accent); }

/* Header & footer */
/* Header: name | menu | light/dark button, all centred on one line, so the button's
   centre matches the name's centre at any size. Spacing is in ems of the name's size
   (--brand-size, 24px); change that one value and the gaps follow.
                           desktop            phones
     above the name:       1em                1em
     name to menu:         2em (sideways)     0.5em (menu is on its own line)
     below the name/menu:  0.8em              0.5em (below the menu)
     menu text:            0.8em of the page text (15px × 0.8 = 12px)
     between menu items:   1.5em of the menu text (12px × 1.5 = 18px) */
.top {
  --brand-size: 1.5rem;
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  column-gap: calc(2 * var(--brand-size)); row-gap: 8px;
  padding: 28px 0 var(--brand-size); border-bottom: 1px solid var(--line);
}
/* Desktop: space above the name is 1em of the name's size (24px); below it, 0.8em (19.2px). */
@media (min-width: 601px) { .top { padding-block: var(--brand-size) calc(0.8 * var(--brand-size)); } }
.brand { font-family: var(--font-display); font-weight: 800; font-size: var(--brand-size); line-height: 1.2; letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none; }
/* The menu links and button have extra tap area above and below; the negative margins stop
   that from making the line taller than the name, so the 1em to the line is measured
   from the name itself. */
.top nav { display: flex; flex-wrap: wrap; gap: 8px 1.5em; margin-block: -8px; font-size: 0.8em; letter-spacing: 0.06em; text-transform: uppercase; }
.top nav a { text-decoration: none; padding: 8px 0; }
/* Desktop: the menu text sits on the same baseline as the name (the button stays centred). */
.top .brand, .top nav { align-self: baseline; }
.top nav a[aria-current="page"] { text-decoration: underline; text-underline-offset: 6px; }
/* Header buttons (light/dark, and HQ on photography pages) sit together in the last column,
   centred on the name (H-06). */
/* 12px apart so the two 44px tap areas meet without overlapping. */
.top-actions { display: flex; align-items: center; gap: 12px; }
/* 32px circles (Brady, 2026-10-22; were 44px). The invisible ::before keeps a 44px tap area. */
.theme-toggle, .hq-toggle, .all-toggle, .random-toggle {
  position: relative; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; margin-block: -8px; padding: 0; background: transparent; color: var(--fg);
  border: 1px solid var(--line); border-radius: 50%; cursor: pointer;
}
/* Hover outline only with a mouse; on touch screens it would stick after a tap. */
@media (hover: hover) and (pointer: fine) { .theme-toggle:hover, .hq-toggle:hover, .all-toggle:hover, .random-toggle:hover { border-color: var(--fg); } }
/* All photos toggle (PH-09): dashed-folder icon in the round button; filled when on (the all-photos page). */
.all-toggle svg { display: block; }
.all-toggle[aria-pressed="true"] { background: var(--fg); color: var(--bg); border-color: var(--fg); }
/* HQ toggle (VW-10): "HQ" in the round button; filled when on. */
.theme-toggle::before, .hq-toggle::before, .all-toggle::before, .random-toggle::before { content: ""; position: absolute; inset: -7px; border-radius: 50%; }
.hq-mark { font-family: var(--font-body); font-size: 12px; font-weight: 600; letter-spacing: 0.04em; line-height: 1; }
.hq-toggle[aria-pressed="true"] { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.hq-toggle[hidden] { display: none; }
/* Random order toggle (PH-12): shuffle icon; filled when on, like HQ and All photos. */
.random-toggle svg { display: block; }
.random-toggle[aria-pressed="true"] { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.random-toggle[hidden] { display: none; }
/* Narrow screens: name and light/dark button share the first line, menu below. */
@media (max-width: 600px) {
  /* Phones: 1em above the name, 0.5em between name and menu, 0.5em below the menu
     (em of the name's size: 24px, 12px, 12px). */
  /* Tighter on phones so four header buttons fit beside the name (Brady, 2026-10-25, PH-12):
     12px name → buttons (16px before), 8px between buttons (12px on desktop). */
  .top { grid-template-columns: 1fr auto; column-gap: 12px; row-gap: calc(0.5 * var(--brand-size)); padding: var(--brand-size) 0 calc(0.5 * var(--brand-size)); }
  .top-actions { gap: 8px; }
  .top nav { grid-column: 1 / -1; grid-row: 2; align-self: center; }
  .top .brand { align-self: center; }
}
.theme-toggle svg { display: block; width: 16px; height: 16px; }
.theme-toggle[hidden] { display: none; }
/* The icon shows the mode you'd switch to: a moon in light mode, a sun in dark mode. */
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
.foot {
  margin-top: 96px; padding: 28px 0 40px; border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px;
  font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
.foot a { color: var(--muted); }
.foot-links { display: flex; gap: 16px; }

/* Home hero & recent photos (HM-02: just the photos, no film strip) */
.hero { padding: 0 0 40px; display: flex; flex-direction: column; gap: 28px; }
.display { font-size: clamp(56px, 11vw, 148px); line-height: 0.9; letter-spacing: -0.02em; }
.lede { max-width: 640px; color: var(--fg-soft); font-size: 17px; }
.recent { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.recent-photo { display: block; aspect-ratio: 3 / 2; background: var(--frame); overflow: hidden; }
.recent-photo img { width: 100%; height: 100%; object-fit: cover; }

/* Sections */
.section { padding-top: 96px; }
.section.first { padding-top: 0; }
.section-head {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 12px;
  border-bottom: 1px solid var(--line); padding-bottom: 14px; margin-bottom: 28px;
}
/* Path heading on every page below a menu section (G-11): "Soul /", "Soul / hey",
   "NTHU course helper". Small grey directory style (the old breadcrumb look); it is the h1. */
/* No line under the path heading (Brady, 2026-10-22): content starts 28px below the path or tags. */
.path-head { border-bottom: 0; padding-bottom: 0; }
.path-head h1 {
  min-width: 0; overflow-wrap: anywhere;
  font-family: var(--font-body); font-weight: 400; font-size: 24px; line-height: 1.4; color: var(--muted);
}
.path-head .path-parent { color: var(--muted); }
.path-head .path-parent:hover { color: var(--strong); }
.tags { flex-basis: 100%; display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 4px 0 0; padding: 0; }
/* Tags: soft grey filled pills, no border, written as typed with the first letter capitalised (Brady, 2026-10-21). */
.tags li { display: block; font-size: 12px; line-height: 1.5; color: var(--tag-fg); background: var(--tag-bg); border-radius: 6px; padding: 4px 10px; }
.tags li::first-letter { text-transform: uppercase; }
/* Phones (after the rules above so it wins): path smaller so most paths fit on one line (Brady, 2026-10-21). Same 600px breakpoint as the header. */
@media (max-width: 600px) {
  .path-head h1 { font-size: 18px; }
  /* Tags smaller on phones too (Brady, 2026-10-21) */
  .tags { gap: 6px; }
  .tags li { font-size: 11px; padding: 3px 8px; border-radius: 5px; }
}
.more { margin-top: 20px; font-size: 13px; }
.empty-note { color: var(--muted); padding: 48px 0; }

/* Category cards */
/* Fixed columns: 3 (2 on phones), so 9 categories fill three even rows and a lone card stays card-sized. */
.categories { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gap); }
@media (max-width: 520px) {
  .categories { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
  .category-meta { font-size: 11px; }
}
.category { display: flex; flex-direction: column; gap: 10px; text-decoration: none; }
.frame { display: flex; align-items: center; justify-content: center; aspect-ratio: 4 / 5; background: var(--frame); overflow: hidden; }
.frame img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s ease; }
.category:hover .frame img { transform: scale(1.03); }
.placeholder { color: var(--muted); font-size: 12px; }
.category-meta { display: flex; justify-content: space-between; font-size: 13px; }

/* Album cards (category page) */

/* Contact sheet (category and album pages) */
/* Photo grid (PH-04): straight on the page, no grey panel or inner padding (Brady, 2026-10-23),
   so it lines up with the heading and the page edges. */
/* Space between photos (Brady, 2026-10-24; was 18px both ways). lightbox.js reads both.
   --sheet-gap-x: between columns. --sheet-gap-y: from a photo's caption (or the photo) to the next photo below. */
.sheet { --sheet-gap-x: 12px; --sheet-gap-y: 8px; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sheet-gap-y) var(--sheet-gap-x); }
/* Photo → its caption: 2px (Brady, 2026-10-24: tighter, was 6px). Caption → next photo stays --sheet-gap. */
.sheet-frame { margin: 0; display: flex; flex-direction: column; gap: 2px; }
.sheet-frame a { display: block; aspect-ratio: 3 / 2; background: var(--frame); overflow: hidden; }
.sheet-frame img { width: 100%; height: 100%; object-fit: cover; }
.sheet-frame figcaption { display: flex; justify-content: space-between; gap: 10px; font-size: 11px; letter-spacing: 0.08em; overflow: hidden; white-space: nowrap; }
/* Photo tile caption (PH-05, Brady 2026-10-25): title left (cut with … if long), date right. */
.sheet-frame .cap-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--fg-soft); }
.sheet-frame .cap-date { flex-shrink: 0; margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Ordered masonry (PH-11): when photos have sizes, each keeps its own shape (--r = w/h; 3:2 if
   unknown). Without JavaScript: the same grid, uncropped, in order. With it, lightbox.js places
   each cell absolutely: 3 columns from 900px of grid width, 2 from 560px, else 1; gaps = --sheet-gap-x / --sheet-gap-y. */
.masonry { align-items: start; }
.masonry .sheet-frame a { aspect-ratio: var(--r, 3 / 2); }
.masonry.is-placed { display: block; position: relative; }
.masonry.is-placed > .sheet-frame { position: absolute; top: 0; left: 0; width: var(--col-w); }
/* Album among the photos (PH-10): folder badge with photo count on the cover. Fixed dark
   see-through pill so it reads on any photo (white text ≥ 5.4:1 even over a white photo). */
.album-frame a { position: relative; }
.album-frame .placeholder { display: flex; align-items: center; justify-content: center; height: 100%; font-size: 11px; color: var(--muted); }
.album-badge { position: absolute; left: 8px; top: 8px; display: inline-flex; align-items: center; gap: 5px; padding: 4px 7px;
  border-radius: 6px; background: rgba(15, 20, 23, 0.62); color: #ffffff; font-size: 12px; line-height: 1; font-variant-numeric: tabular-nums; }
.album-frame figcaption { justify-content: space-between; }
.album-frame .album-title { color: var(--fg); overflow: hidden; text-overflow: ellipsis; }
.album-frame figcaption .muted { flex-shrink: 0; }

/* Projects */
.project-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 8px 32px; align-items: baseline;
  padding: 22px 0; border-bottom: 1px solid var(--line); text-decoration: none;
}
.project-title { font-family: var(--font-display); font-weight: 500; font-size: 22px; }
.project-summary { color: var(--fg-soft); }
.prose { max-width: 720px; display: flex; flex-direction: column; gap: 18px; margin-top: 28px; color: var(--fg-soft); font-size: 16px; }
.project .lede { margin-top: 4px; }
.links { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 36px; }

/* Buttons */
.button {
  display: inline-flex; align-items: center; gap: 10px; min-height: 48px; padding: 0 22px;
  background: var(--accent); color: var(--on-accent); text-decoration: none; font-weight: 500; letter-spacing: 0.04em;
}
.button:hover { color: var(--on-accent); filter: brightness(1.08); }
.button.ghost { background: transparent; color: var(--fg); border: 1px solid var(--line); }
.button.ghost:hover { border-color: var(--fg); color: var(--strong); filter: none; }

/* About */
.about { padding-top: 96px; display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 32px; align-items: start; }
.section.first .about { padding-top: 0; }
.portrait { aspect-ratio: 1 / 1; max-width: 360px; }
.about-text { display: flex; flex-direction: column; gap: 16px; color: var(--fg-soft); }
.about-text h1, .about-text h2 { color: var(--fg); }

/* Lightbox (assets/lightbox.js). Always dark, whatever the page mode, so photos and
   controls read the same on the near-black background. */
.lightbox {
  --fg: #ecebe6; --surface: #1a1a18; --line: #3a3935;
  --lb-muted: #a8a7a1; /* secondary viewer text: 7.6:1 or more on the backdrop */
  --lightbox-bg: rgba(8, 8, 7, 0.95); /* lower the last number to see more of the page */
  position: fixed; inset: 0; margin: 0; border: 0; padding: 0;
  width: 100%; height: 100%; max-width: none; max-height: none;
  /* One see-through layer. The shadow carries it well past every edge, so no strip of
     undimmed page shows under a phone browser's toolbars. */
  background: var(--lightbox-bg); box-shadow: 0 0 0 100vmax var(--lightbox-bg);
  overscroll-behavior: contain;
  color-scheme: dark;
}
.lightbox:focus { outline: none; }
.lightbox::backdrop { background: transparent; }
/* While the viewer is open the page behind it can't scroll (assets/lightbox.js). */
html.lightbox-open, html.lightbox-open body { overflow: hidden; overscroll-behavior: none; }
.lightbox-inner { width: 100%; height: 100%; display: grid; grid-template-rows: minmax(0, 1fr) auto; padding: 20px; gap: 12px; color: var(--fg); font-family: var(--font-body); }
.lightbox img { width: 100%; height: 100%; min-height: 0; object-fit: contain; }
.lightbox-bar { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px; font-size: 13px; }
/* Viewer caption (VW-02, Brady 2026-10-25): bottom left; title in the site's body text, then date and
   description smaller and dimmer. Empty parts are hidden. Long descriptions stop at 3 lines. */
.lightbox-caption { min-width: 0; max-width: 72ch; display: flex; flex-direction: column; gap: 3px; }
.lightbox-caption [hidden] { display: none; }
.lightbox .lb-title { font-size: 15px; line-height: 1.4; color: var(--fg); overflow-wrap: anywhere; }
.lightbox .lb-date, .lightbox .lb-desc { font-size: 12px; line-height: 1.5; letter-spacing: 0.02em; color: var(--lb-muted); }
.lightbox .lb-date { font-variant-numeric: tabular-nums; }
.lightbox .lb-desc { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.lightbox-bar .controls { display: flex; gap: 8px; }
.lightbox button {
  min-width: 44px; min-height: 44px; background: var(--surface); color: var(--fg); border: 1px solid var(--line);
  font: inherit; cursor: pointer; padding: 0 14px;
  display: inline-flex; align-items: center; justify-content: center;
}
.lightbox button svg { display: block; }
/* ×: a bare 30px cross centred in a 44×44 button, no box (Brady, 2026-10-21). 44×44 keeps it easy to hit. */
.lightbox button.close { width: 44px; height: 44px; padding: 0; border: 0; background: transparent; opacity: 0.7; transition: opacity 0.15s; }
/* Dimmed at rest; full brightness while pressed (mouse or a tap on a phone) or keyboard-focused.
   .pressed is set by lightbox.js; it brightens at once (no fade) so even a quick tap shows it. */
.lightbox button.close:active, .lightbox button.close:focus-visible { opacity: 1; }
.lightbox button.close.pressed { opacity: 1; transition: none; }
/* ←/→ buttons: hidden unless reached with the keyboard (VW-02, VW-07). */
.lightbox .step:not(:focus-visible) {
  position: absolute; width: 1px; height: 1px; min-width: 0; min-height: 0; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* Loading (VW-08): the small preview stands in, slightly dimmed, with a spinner and "Loading"
   over it. Both fade in only after 150ms, so photos that arrive quickly don't flash it. */
.lightbox img { grid-row: 1; grid-column: 1; transition: opacity 0.15s; }
.lightbox.is-loading img { opacity: 0.6; transition-delay: 150ms; }
.lightbox-loading {
  grid-row: 1; grid-column: 1; /* same cell as the photo, so it sits centred on it */
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg);
  pointer-events: none; opacity: 0; transition: opacity 0.15s;
}
.lightbox.is-loading .lightbox-loading { opacity: 1; transition-delay: 150ms; }
.lightbox-loading > span:last-child { background: rgba(8, 8, 7, 0.6); padding: 2px 8px; }
.lightbox-spinner {
  width: 28px; height: 28px; border-radius: 50%;
  border: 2px solid rgba(236, 235, 230, 0.3); border-top-color: var(--fg);
  animation: lightbox-spin 0.8s linear infinite;
}
@keyframes lightbox-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .lightbox-spinner { animation: none; border-top-color: rgba(236, 235, 230, 0.3); } }
/* Mouse cursor hints (set by lightbox.js): ← over the left half of the screen, → over the right half. */
.lightbox[data-side="prev"] { cursor: w-resize; }
.lightbox[data-side="next"] { cursor: e-resize; }
/* Hover outline only with a mouse; on touch screens it would stick after a tap. */
@media (hover: hover) and (pointer: fine) {
  .lightbox button:hover { border-color: var(--fg); }
  .lightbox button.close:hover { opacity: 1; } /* mouse hover: full brightness */
}

/* Private pages (rendered by the Worker in src/worker.js with these pc-* classes) */
.pc-main { max-width: 860px; margin: 0 auto; padding: 40px var(--gap) 56px; }
.pc-title-row { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; }
.pc-title { font-size: clamp(32px, 5vw, 48px); min-width: 0; overflow-wrap: anywhere; }
/* Toggle sits at the right edge of the postcard column, whatever the title length. */
.pc-title-row .theme-toggle { flex: none; margin-inline-start: auto; }
.pc-kicker { display: block; margin-bottom: 10px; }
.pc-muted { color: var(--muted); font-size: 13px; }
.pc-list { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--line); }
.pc-list li { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 16px; padding: 16px 0; border-bottom: 1px solid var(--line); }
.pc-list a { font-family: var(--font-display); font-size: 20px; text-decoration: none; }
.pc-back { display: inline-block; margin-bottom: 16px; font-size: 13px; color: var(--muted); }
.pc-pdf { width: 100%; height: 75vh; border: 1px solid var(--line); background: #fff; }
.pc-viewer { margin: 16px 0; display: flex; flex-direction: column; gap: 12px; min-height: 120px; }
.pc-page { display: block; width: 100%; height: auto; background: #fff; box-shadow: 0 2px 16px var(--page-shadow); }
.pc-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 20px; }
/* Admin pages (/admin/, Brady only; markup in src/admin.js) reuse the postcard layout. Their plain
   form <button> ("Rebuild site") looks like the postcard buttons; their <h2> section headings get a rule. */
.pc-button, .pc-main form > button {
  display: inline-flex; align-items: center; min-height: 48px; padding: 0 22px; background: var(--accent); color: var(--on-accent);
  text-decoration: none; font: inherit; font-weight: 500; border: 0; cursor: pointer;
}
.pc-button:hover, .pc-main form > button:hover { color: var(--on-accent); filter: brightness(1.08); }
.pc-button:disabled { opacity: 0.6; cursor: progress; }
.pc-button[hidden] { display: none; }
.pc-main > h2 { font-family: var(--font-display); font-weight: 500; font-size: 22px; line-height: 1.2; margin: 40px 0 12px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
.pc-main > form { margin: 16px 0 8px; }

/* Admin dashboard (/admin/dashboard, AD-02; markup in src/dashboard.js). Strict CSP: all styling here. */
.pc-main > h3 { font-size: 12px; font-weight: 400; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin: 28px 0 8px; }
.pc-main code { font-size: 0.92em; padding: 1px 5px; border-radius: 4px; background: var(--tag-bg); color: var(--tag-fg); }
.dash-table { width: 100%; border-collapse: collapse; font-size: 13px; margin: 8px 0 12px; }
.dash-table th, .dash-table td { padding: 9px 14px 9px 0; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; overflow-wrap: break-word; }
.dash-table tr > :last-child { padding-right: 0; }
.dash-table thead th { font-size: 11px; font-weight: 400; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); border-bottom-color: var(--fg); padding-top: 0; }
.dash-table tbody tr:first-child > * { border-top: 1px solid var(--line); }
.dash-table thead + tbody tr:first-child > * { border-top: 0; }
.dash-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Traffic tables (label + count, under an h3): long page paths may break anywhere; counts never break. */
h3 + .dash-table td:first-child { overflow-wrap: anywhere; }
h3 + .dash-table td.num { white-space: nowrap; }
/* Site overview (straight under its h2, no header row): short labels on one line, values read as sentences. */
h2 + .dash-table:not(:has(thead)) td:first-child { width: 1%; white-space: nowrap; color: var(--muted); }
h2 + .dash-table:not(:has(thead)) td, h2 + .dash-table:not(:has(thead)) td.num { text-align: left; }
h2 + .dash-table tbody tr:first-child > * { border-top: 0; }
/* Postcard opens (header row, no numbers): date and kind stay on one line. */
.dash-table:has(thead):not(:has(td.num)) td:nth-child(1), .dash-table:has(thead):not(:has(td.num)) td:nth-child(4) { white-space: nowrap; }
/* A header over a number column lines up with the numbers. */
.dash-table:has(tbody td.num:nth-child(2)) thead th:nth-child(2),
.dash-table:has(tbody td.num:nth-child(3)) thead th:nth-child(3) { text-align: right; }
.dash-table td.pc-muted { font-size: 12px; }
/* Usage vs free limits (the 4-column table with numbers): amounts never break; the note takes what's left. */
.dash-table:has(thead):has(td.num) td.num { white-space: nowrap; }
.dash-table:has(thead):has(td.num) td.pc-muted { width: 45%; }
.pc-main > .pc-muted strong { color: var(--fg); font-weight: 600; }
/* Page views per day: bars in the accent colour; labels muted, with a halo so they stay readable over bars. */
.dash-chart { display: block; height: auto; margin: 18px 0 6px; color: var(--accent); overflow: visible; }
.dash-chart rect { transition: opacity 0.15s; }
@media (hover: hover) and (pointer: fine) { .dash-chart rect:hover { opacity: 1; } }
.dash-chart text { fill: var(--muted); font-family: var(--font-body); paint-order: stroke; stroke: var(--bg); stroke-width: 3px; stroke-linejoin: round; }
@media (max-width: 600px) {
  /* The chart scales with its width, so its labels get larger units on small screens (≈11px on screen). */
  .dash-chart text { font-size: 13px; stroke-width: 4px; }
  /* 4-column tables become stacked rows on phones instead of pushing the page wider. */
  .dash-table:has(thead) thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .dash-table:has(thead), .dash-table:has(thead) tbody { display: block; }
  .dash-table:has(thead) tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line); }
  .dash-table:has(thead) tbody tr:first-child { border-top: 1px solid var(--line); }
  .dash-table:has(thead) td { display: block; padding: 0; border: 0; white-space: normal; overflow-wrap: anywhere; }
  .dash-table:has(thead):has(td.num) td.num { white-space: nowrap; }
  /* Postcard opens: When … kind on the first line, then friend, then postcard. */
  .dash-table:has(thead):not(:has(td.num)) td:nth-child(1) { grid-area: 1 / 1; color: var(--muted); font-size: 12px; }
  .dash-table:has(thead):not(:has(td.num)) td:nth-child(4) { grid-area: 1 / 2; color: var(--muted); font-size: 12px; }
  .dash-table:has(thead):not(:has(td.num)) td:nth-child(2) { grid-area: 2 / 1 / 3 / 3; margin-top: 4px; }
  .dash-table:has(thead):not(:has(td.num)) td:nth-child(3) { grid-area: 3 / 1 / 4 / 3; }
  /* Usage: label on its own line, then used / free and the percentage, then the note. */
  .dash-table:has(thead):has(td.num) td:nth-child(1) { grid-area: 1 / 1 / 2 / 3; }
  .dash-table:has(thead):has(td.num) td:nth-child(2) { grid-area: 2 / 1; text-align: left; margin-top: 4px; }
  .dash-table:has(thead):has(td.num) td:nth-child(3) { grid-area: 2 / 2; margin-top: 4px; }
  .dash-table:has(thead):has(td.num) td:nth-child(4) { grid-area: 3 / 1 / 4 / 3; width: auto; margin-top: 4px; }
  .dash-table:has(thead):has(td.num) td:nth-child(4):empty { display: none; }
}
/* After the 600px block so it wins on the narrowest phones. */
@media (max-width: 480px) { .dash-chart text { font-size: 19px; stroke-width: 5px; } }
.pc-footer { margin-top: 48px; padding-top: 20px; border-top: 1px solid var(--line); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); display: flex; gap: 20px; }
.pc-footer a { color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .frame img { transition: none; }
}
