/* ─────────────────────────────────────────────────────────────────────────────
   theme.css — colour tokens for Dark (default) and Light mode
   Dark values are exactly the site's original colours.
   ───────────────────────────────────────────────────────────────────────────── */
:root {
  --bg-rgb: 17, 17, 17;          /* #111111 (was #141414; a touch darker to offset the texture, Oct 11 2026) */
  --ink-rgb: 201, 211, 199;      /* #c9d3c7 */
  color-scheme: dark;
}
/* Light mode — "Inverted Sage": the dark mode flipped. Its sage text colour becomes the
   page, its charcoal background becomes the text. Alternatives are listed at the bottom. */
:root[data-theme="light"] {
  --bg-rgb: 201, 211, 199;       /* #C9D3C7 */
  --ink-rgb: 20, 20, 20;         /* #141414 */
  color-scheme: light;
}

/* Smooth cross-fade when the visitor switches (enabled after first paint) */
:root.theme-ready body,
:root.theme-ready nav,
:root.theme-ready #detail,
:root.theme-ready #detail-nav,
:root.theme-ready #loader,
:root.theme-ready #bottom-bar,
:root.theme-ready .cols-header {
  transition: background-color .45s ease, color .45s ease;
}

/* ── Theme switch ──
   Desktop: a small pill in the menu bar after the page links, drawn like the
   Carousel / Grid / Index toggle (same hairline outline, same highlight on the active side).
   Phones: the menu bar stays as it was; the switch becomes one more line in the
   bottom-left stack, above Instagram / Vimeo, in the same small caps. ── */
.theme-switch {
  display: inline-flex;
  align-items: center;
  border: 0;
  background: none;
  cursor: pointer;
  color: rgba(var(--ink-rgb), .42);
  -webkit-tap-highlight-color: transparent;
}
.theme-switch .ts-dot {
  flex-shrink: 0; border-radius: 50%;
  border: 1px solid rgba(var(--ink-rgb), .7);
  background: linear-gradient(90deg, rgba(var(--ink-rgb), .7) 50%, transparent 50%);
  transition: transform .45s ease;
}
:root[data-theme="light"] .theme-switch .ts-dot { transform: rotate(180deg); }
.theme-switch [data-v] { transition: color .2s ease, background-color .25s ease, border-color .25s ease; }
.theme-switch [data-v].on { color: rgba(var(--ink-rgb), .92); }
.theme-switch:focus-visible { outline: 1px solid rgba(var(--ink-rgb), .5); outline-offset: 3px; }

/* desktop pill */
nav .theme-switch {
  align-self: flex-start;
  margin: -9px 0 -7px 4px;     /* the pill is taller than a menu word: keep the menu row's height and centre it on the text */
  padding: 2px;
  gap: 0;
  border: 1px solid rgba(var(--ink-rgb), .22);
  border-radius: 999px;
  background: rgba(var(--ink-rgb), .04);
  font: 300 10.5px/1 'Inter', sans-serif;
  letter-spacing: .05em;
  transition: border-color .2s ease;
}
nav .theme-switch:hover { border-color: rgba(var(--ink-rgb), .38); }
nav .theme-switch .ts-dot { width: 9px; height: 9px; margin: 0 3px 0 8px; }
nav .theme-switch .ts-sep { display: none; }
nav .theme-switch [data-v] {
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid transparent;
}
nav .theme-switch [data-v].on {
  background: rgba(var(--ink-rgb), .14);
  border-color: rgba(var(--ink-rgb), .28);
}
nav .theme-switch:hover [data-v].on { background: rgba(var(--ink-rgb), .2); }
nav .theme-switch:hover [data-v]:not(.on) { color: rgba(var(--ink-rgb), .7); }

/* phone version: a small-caps line in the bottom-right corner, level with Vimeo
   (under the project counter on the homepage). Hidden on desktop. */
.theme-switch.ts-line { display: none; }
@media (max-width: 768px) {
  nav .theme-switch { display: none; }
  .theme-switch.ts-line {
    display: inline-flex;
    position: fixed;
    right: var(--ts-r, 20px);
    bottom: var(--ts-b, 20px);
    z-index: 210;
    gap: 4px;
    padding: 0;
    font: 400 8.5px/1 'Inter', sans-serif;
    letter-spacing: .14em;
    text-transform: uppercase;
  }
  .theme-switch.ts-line::after {        /* bigger tap target, invisible */
    content: ''; position: absolute; inset: -12px -10px;
  }
  .ts-line .ts-dot { width: 8px; height: 8px; margin-right: 3px; }
  .ts-line .ts-sep { opacity: .6; }
}

/* Embedded players (Vimeo / YouTube) don't declare a colour scheme. If the page says "dark"
   and the player doesn't, browsers paint an opaque light backdrop behind the iframe, which
   shows as a thin white/grey edge around the video. Matching the player's scheme removes it. */
iframe { color-scheme: normal; }

/* ── Scrollbars: one thin, quiet style everywhere (macOS otherwise switches between
      slim overlay bars and chunky always-on bars depending on the mouse/trackpad setting) ── */
* { scrollbar-width: thin; scrollbar-color: rgba(var(--ink-rgb), .22) transparent; }
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(var(--ink-rgb), .2); border-radius: 999px; }
::-webkit-scrollbar-thumb:hover { background: rgba(var(--ink-rgb), .38); }
::-webkit-scrollbar-corner { background: transparent; }

/* ── Light-mode adjustments ── */
/* Arrow icon next to Instagram / Vimeo: the dark-mode invert trick would vanish on paper */
:root[data-theme="light"] .bl-arrow-img { filter: none; mix-blend-mode: multiply; opacity: .62; }
:root[data-theme="light"] .bl-link:hover .bl-arrow-img { opacity: .9; }

/* About page: list numbers, letters and contact emails were too faint on the light page */
:root[data-theme="light"] .entry-num { color: rgba(var(--ink-rgb), .62); font-weight: 400; }
:root[data-theme="light"] .alpha-letter { color: rgba(var(--ink-rgb), .45); }
:root[data-theme="light"] .entry-name { color: rgba(var(--ink-rgb), .68); }
:root[data-theme="light"] .contact-email { color: rgba(var(--ink-rgb), .72); }
:root[data-theme="light"] .contact-email:hover { color: rgba(var(--ink-rgb), .95); }
:root[data-theme="light"] .contact-label { color: rgba(var(--ink-rgb), .5); }
:root[data-theme="light"] .display-role { color: rgba(var(--ink-rgb), .5); }

/* Stills open in a dark room in both modes — slightly denser backdrop over the light page */
:root[data-theme="light"] #lightbox { background: rgba(12, 12, 12, .97); }
:root[data-theme="light"] #still-lightbox.open { background: rgba(20, 20, 20, .985); }

/* ── Other light palettes considered (swap the two values in the light block above) ──
   Stone   — gallery-wall warm grey:  --bg-rgb 216,214,208  --ink-rgb 27,26,24
   Bone    — warm cream:              --bg-rgb 232,226,214  --ink-rgb 35,32,27
   Paper   — first version (near-white sage): --bg-rgb 236,238,233  --ink-rgb 26,28,25 */

/* ── Background texture: scan lines + film grain (theme.js TEXTURE_DEFAULT; v27, Oct 11 2026)
   · Class .ft-texture, made by theme.js texMake(). Other pages: one layer behind the content.
     Homepage: the 3D canvas draws its own copy (portfolio.html); CSS layers only cover the
     loading screen and the index view (positions set in portfolio.html). Videos, stills and the
     index hover preview are never textured.
   · Both textures are small images drawn once by theme.js (no SVG filters, nothing redrawn
     per frame) and sized in whole screen pixels, so line spacing is perfectly even at any
     zoom level or screen density (no moiré).
   · Scan lines: dark, static, ~1.5 screen pixels thick every 3 page pixels; only ever darken.
   · Grain: one layer of single-pixel specks (light + dark, average colour unchanged), shifted
     to a new position 6×/s by the compositor. Still for reduced-motion visitors. */
.ft-texture { position: fixed; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; contain: strict; }
/* Other pages: one layer just behind ALL page content (images, videos, text stay untouched), above
   the page colour, which html keeps while body goes transparent. */
#ft-texture { z-index: -1; }
:root[data-tex="film"] body { background: transparent; }
.ft-texture i, .ft-texture canvas { position: absolute; display: none; }
:root[data-tex="film"] .ft-texture i, :root[data-tex="film"] .ft-texture canvas { display: block; }

:root { --tex-k: 1; }
@media (pointer: coarse) { :root { --tex-k: .7; } }   /* phones/tablets: texture 30% lighter */
.ft-texture .t-scan { top: 0; left: 0; width: 100%; image-rendering: pixelated; opacity: var(--tex-k); }
:root[data-theme="light"] .ft-texture .t-scan { opacity: calc(.11 * var(--tex-k)); }

.ft-texture .t-grain {
  top: -256px; left: -256px; right: -256px; bottom: -256px;
  background-repeat: repeat;
  will-change: transform;
  animation: ft-grain 1s steps(1, end) infinite;
  opacity: calc(.27 * var(--tex-k));
}
:root[data-theme="light"] .ft-texture .t-grain { opacity: calc(.32 * var(--tex-k)); }
@keyframes ft-grain {
  0%     { transform: translate3d(0, 0, 0); }
  16.66% { transform: translate3d(-97px, 61px, 0); }
  33.33% { transform: translate3d(53px, -118px, 0); }
  50%    { transform: translate3d(-141px, -37px, 0); }
  66.66% { transform: translate3d(129px, 89px, 0); }
  83.33% { transform: translate3d(-29px, 147px, 0); }
  100%   { transform: translate3d(0, 0, 0); }
}
.ft-texture .t-vig {
  inset: 0; opacity: var(--tex-k);
  background: radial-gradient(ellipse 85% 80% at 50% 50%, transparent 60%, rgba(0, 0, 0, .18) 100%);
}
:root[data-theme="light"] .ft-texture .t-vig {
  background: radial-gradient(ellipse 85% 80% at 50% 50%, transparent 60%, rgba(20, 20, 20, .05) 100%);
}
@media (prefers-reduced-motion: reduce) { .ft-texture i { animation: none !important; } }
