/* ============================================================
   Practice — Global styles
   Matches the Practice Design System (black & red zen).
   Tokens mirror design-system/Practice Design System.html.
   ============================================================ */

:root {
  /* Color */
  --bg: #000000;
  --bg-raised: #0b0b0b;
  --ink: #ece7dc;
  /* Tokens tuned for readability on dim screens / cheap TN panels.
     Raising the faint shades makes meta text visible at half-brightness
     without blowing out the zen aesthetic. ink-line bumped a hair so
     dividers don't disappear on LCDs that crush near-black. */
  --ink-soft: rgba(236, 231, 220, 0.86);
  --ink-faint: rgba(236, 231, 220, 0.64);
  --ink-line: rgba(236, 231, 220, 0.30);
  --brush: #ffffff;
  --red: #b22222;
  --red-hi: #d64545;
  --red-lo: #8a1a1a;
  --red-glow: rgba(178, 34, 34, 0.35);
  --vignette: rgba(40, 10, 10, 0.18);

  /* Type */
  --f-serif: 'Cormorant Garamond', 'Noto Serif JP', Georgia, serif;
  --f-jp: 'Noto Serif JP', serif;
  --f-mono: 'JetBrains Mono', ui-monospace, monospace;

  /* Motion */
  --ease-zen: cubic-bezier(.2,.7,.2,1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  width: 100%;
  min-height: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--f-serif);
  /* Base body font-size set explicitly so child elements that use
     em / rem scale predictably, and so the page is legible at default
     browser zoom on cheap low-DPI panels. */
  font-size: 16px;
  line-height: 1.55;
  font-weight: 300;
  overflow-x: hidden;
  background-image: radial-gradient(ellipse at 50% 0%, var(--vignette) 0%, transparent 60%);
  /* Optimize how the browser renders glyphs — meaningful on Chromebooks
     where the default rasterizer can be a touch fuzzy. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Large displays (≥1600px wide): bump the base size a notch so the body
   serif doesn't feel cramped at long reading distances. */
@media (min-width: 1600px) {
  body { font-size: 17px; }
}

a { color: inherit; text-decoration: none; }
button, input, select { font: inherit; color: inherit; }

/* Optional very subtle grid grain used on some pages */
.grain {
  pointer-events: none;
  position: fixed;
  inset: 0;
  z-index: 0;
  opacity: 0.04;
  background-image:
    linear-gradient(rgba(236, 231, 220, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(236, 231, 220, 0.04) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(circle at center, black 40%, transparent 100%);
}

/* ============================================================
   Navbar — grain dot in top-left, expands on hover into a menu
   No header inside the expanded menu. Matches Practice lander.
   ============================================================ */
.nv {
  position: fixed;
  top: 22px;
  left: 22px;
  /* Above the viewer modal (z:50) and any settings panel — so users can
     always reach the menu, even with a game/app open in the iframe. */
  z-index: 100;
  display: flex;
  align-items: flex-start;
  padding: 10px;
  margin: -10px;
}

.nv-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  position: relative;
  flex-shrink: 0;
  transition: transform 0.5s var(--ease-zen), opacity 0.4s ease;
  opacity: 0.9;
}
.nv-dot::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0) 70%);
  opacity: 0.6;
  animation: nvPulse 4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes nvPulse {
  0%,100% { transform: scale(1);   opacity: 0.4; }
  50%     { transform: scale(1.3); opacity: 0.7; }
}
.nv-grain {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: 50%;
}

.nv-menu {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
  max-height: 0;
  max-width: 0;
  opacity: 0;
  transform: translate(-6px, -4px);
  transition:
    max-height 0.45s var(--ease-zen),
    max-width 0.45s var(--ease-zen),
    opacity 0.3s ease,
    transform 0.45s var(--ease-zen),
    margin-left 0.45s var(--ease-zen),
    padding 0.45s var(--ease-zen),
    background 0.3s ease,
    border-color 0.3s ease;
  pointer-events: none;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  backdrop-filter: blur(0);
  -webkit-backdrop-filter: blur(0);
}

.nv:hover .nv-menu,
.nv:focus-within .nv-menu {
  padding: 6px;
  background: rgba(8, 8, 8, 0.82);
  border-color: var(--ink-line);
  backdrop-filter: blur(12px) saturate(1.1);
  -webkit-backdrop-filter: blur(12px) saturate(1.1);
}

.nv:hover .nv-dot,
.nv:focus-within .nv-dot {
  transform: scale(0.6);
  opacity: 0.5;
}

.nv:hover .nv-menu,
.nv:focus-within .nv-menu {
  max-height: 500px;
  max-width: 320px;
  opacity: 1;
  transform: translate(0,0);
  margin-left: 14px;
  pointer-events: auto;
}

.nv-menu li {
  transform: translateY(-6px);
  opacity: 0;
  transition: transform 0.4s var(--ease-zen), opacity 0.3s ease;
}
.nv:hover .nv-menu li,
.nv:focus-within .nv-menu li { transform: translateY(0); opacity: 1; }
.nv:hover .nv-menu li:nth-child(1), .nv:focus-within .nv-menu li:nth-child(1) { transition-delay: 0.05s; }
.nv:hover .nv-menu li:nth-child(2), .nv:focus-within .nv-menu li:nth-child(2) { transition-delay: 0.10s; }
.nv:hover .nv-menu li:nth-child(3), .nv:focus-within .nv-menu li:nth-child(3) { transition-delay: 0.15s; }
.nv:hover .nv-menu li:nth-child(4), .nv:focus-within .nv-menu li:nth-child(4) { transition-delay: 0.20s; }
.nv:hover .nv-menu li:nth-child(5), .nv:focus-within .nv-menu li:nth-child(5) { transition-delay: 0.25s; }
.nv:hover .nv-menu li:nth-child(6), .nv:focus-within .nv-menu li:nth-child(6) { transition-delay: 0.30s; }
.nv:hover .nv-menu li:nth-child(7), .nv:focus-within .nv-menu li:nth-child(7) { transition-delay: 0.35s; }
.nv:hover .nv-menu li:nth-child(8), .nv:focus-within .nv-menu li:nth-child(8) { transition-delay: 0.40s; }
.nv:hover .nv-menu li:nth-child(9), .nv:focus-within .nv-menu li:nth-child(9) { transition-delay: 0.45s; }

.nv-menu a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 12px 7px 8px;
  color: var(--ink-soft);
  text-decoration: none;
  font-family: var(--f-serif);
  font-weight: 300;
  font-size: 14px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border-radius: 6px;
  transition: color 0.25s ease, background 0.25s ease, letter-spacing 0.3s ease;
  white-space: nowrap;
}
.nv-menu a:hover {
  color: var(--ink);
  background: rgba(255,255,255,0.04);
  letter-spacing: 0.22em;
}
.nv-menu a:hover svg { color: var(--red); }
.nv-menu a.is-active {
  color: var(--ink);
}
.nv-menu a.is-active svg { color: var(--red); }

.nv-menu svg {
  width: 14px;
  height: 14px;
  color: var(--ink-faint);
  transition: color 0.25s ease;
  flex-shrink: 0;
}

/* Mobile/touch: tap the dot to toggle the menu */
@media (pointer: coarse) {
  .nv.is-open .nv-menu {
    max-height: 500px;
    max-width: 320px;
    opacity: 1;
    transform: translate(0,0);
    margin-left: 14px;
    pointer-events: auto;
    padding: 6px;
    background: rgba(8, 8, 8, 0.82);
    border-color: var(--ink-line);
    backdrop-filter: blur(12px) saturate(1.1);
    -webkit-backdrop-filter: blur(12px) saturate(1.1);
  }
  .nv.is-open .nv-dot {
    transform: scale(0.6);
    opacity: 0.5;
  }
  .nv.is-open .nv-menu li {
    transform: translateY(0);
    opacity: 1;
  }
}

/* Reduced motion — no animations, but the menu still appears on hover/focus
   with its backdrop so the nav is usable and readable.
   Gated behind :not(.force-motion) so the user can override from Settings. */
@media (prefers-reduced-motion: reduce) {
  html:not(.force-motion) .nv-dot::after { animation: none; opacity: 0.3; }
  html:not(.force-motion) .nv-menu,
  html:not(.force-motion) .nv-menu li,
  html:not(.force-motion) .nv-dot { transition: none !important; }
  html:not(.force-motion) .nv:hover .nv-menu,
  html:not(.force-motion) .nv:focus-within .nv-menu,
  html:not(.force-motion) .nv.is-open .nv-menu {
    transform: none !important;
    padding: 6px !important;
    background: rgba(8, 8, 8, 0.88) !important;
    border-color: var(--ink-line) !important;
  }
  html:not(.force-motion) .nv-menu a { transition: none !important; letter-spacing: 0.18em !important; }
  html:not(.force-motion) .nv-menu a:hover { letter-spacing: 0.18em !important; }
}

/* Explicit user opt-out of motion (same as reduced-motion, but user-chosen) */
html.force-still .nv-dot::after { animation: none; opacity: 0.3; }
html.force-still .nv-menu,
html.force-still .nv-menu li,
html.force-still .nv-dot,
html.force-still .nv-menu a { transition: none !important; }

/* ============================================================
   Chromebook + tablet ergonomics
   Most Practice users are on Chromebooks. They have touchscreens,
   modest CPUs, and frequently the 1366×768 panel. These rules:
     - bump iconic-button hit targets to 40px on touch hardware
     - prevent long-press text selection on icon buttons
     - tighten page top/bottom margins on 1366×768 so more is visible
       above the fold without scrolling
     - drop the nav-dot pulse animation in lite mode (CPU win)
   ============================================================ */
@media (pointer: coarse) {
  /* Iconic buttons — give them a real touch target. */
  .viewer-actions button,
  .lookup-frame-actions button,
  .chrome-controls button,
  .chrome-extras a,
  .chrome-extras button,
  .tab-close,
  .tab-add,
  .ai-attach,
  .ai-send,
  .ai-stop,
  .recents-clear {
    min-width: 40px;
    min-height: 40px;
  }
  /* Stop long-press on icon buttons from selecting text or popping the
     OS context menu. */
  .nv-dot,
  .tab,
  .tab-close,
  .tab-add,
  .book-card,
  .catalog-card { -webkit-touch-callout: none; user-select: none; }
}

/* 1366x768 — the most common Chromebook resolution. Trim default page
   chrome so the catalog/grid is visible without an immediate scroll. */
@media (min-width: 1024px) and (max-width: 1440px) and (max-height: 820px) {
  .catalog-page,
  .lookup-page,
  .ai-page,
  .music-page,
  .settings-page {
    margin-top: 88px;
    margin-bottom: 48px;
  }
  .catalog-header h1,
  .lookup-header h1,
  .ai-header h1,
  .music-header h1 {
    font-size: clamp(34px, 4.6vw, 50px);
  }
  .kanji { font-size: clamp(120px, 22vw, 200px); }
}

/* Lite mode auto-triggers on weaker Chromebooks (≤4GB RAM or ≤4 cores).
   Kill non-essential animations to keep the page from chugging. */
html.lite .nv-dot::after { animation: none !important; opacity: 0.4; }
html.lite .ai-thinking { animation-duration: 2.4s; }
html.lite .grain { display: none; }

/* ============================================================
   First-visit "this dot is the menu" hint
   The wrapper, arrow size, note paper, and button are all styled
   inline from script.js — see the tip.style.cssText block there.
   This stylesheet only owns the .is-visible / hover states and the
   small-screen layout override. We deliberately don't keyframe the
   note's transform (rotate) so the inline -1.6deg sticks.
   ============================================================ */
/* When the wrapper goes visible, also flip visibility from hidden →
   visible so the note inside (which would otherwise block hover/click
   on the nav menu underneath while invisible) only becomes interactive
   AFTER the entrance fade. */
.nv-first-tip.is-visible {
  opacity: 1 !important;
  visibility: visible !important;
  transition: opacity 380ms cubic-bezier(0.22, 1, 0.36, 1), visibility 0s !important;
}
.nv-first-tip.is-visible .nv-first-tip-note {
  pointer-events: auto;
}
.nv-first-tip.is-leaving {
  opacity: 0 !important;
  transition-duration: 280ms !important;
}
.nv-first-tip-x:hover {
  background: #1c1614 !important;
  color: #f3edd9 !important;
}

@media (max-width: 600px) {
  /* On narrow screens hide the arrow and stack the note straight
     under the nav dot. */
  .nv-first-tip {
    top: 56px !important;
    left: 22px !important;
    width: calc(100% - 44px) !important;
    max-width: 320px !important;
  }
  .nv-first-tip-arrow { display: none !important; }
  .nv-first-tip-note {
    margin-left: 0 !important;
    margin-top: 0 !important;
    width: auto !important;
  }
}

/* ============================================================
   Shared corner whispers (kanji + italic tagline)
   Optional — include <div class="kanji">練習</div> /
   <div class="corner">...</div> on pages that want them.
   ============================================================ */
.kanji {
  position: fixed;
  top: 6vh;
  right: 6vh;
  font-family: var(--f-jp);
  font-weight: 300;
  color: rgba(236, 231, 220, 0.35);
  font-size: clamp(16px, 1.8vh, 22px);
  letter-spacing: 0.4em;
  writing-mode: vertical-rl;
  z-index: 5;
  pointer-events: none;
}
.corner-note {
  position: fixed;
  bottom: 6vh;
  left: 6vh;
  font-family: var(--f-serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(11px, 1.3vh, 14px);
  color: rgba(236, 231, 220, 0.4);
  letter-spacing: 0.15em;
  z-index: 5;
  pointer-events: none;
}

/* ============================================================
   Landing wordmark — reused on the landing page
   ============================================================ */
.landing-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  position: relative;
  z-index: 1;
  padding: 24px;
}

.landing-wordmark {
  margin: 0;
  display: inline-flex;
  align-items: center;
  color: var(--ink);
  font-family: var(--f-serif);
  font-weight: 300;
  font-size: clamp(2.4rem, 7vw, 5.2rem);
  letter-spacing: 0.62em;
  padding-left: 0.62em; /* tracking comp */
  text-transform: uppercase;
  line-height: 1;
}

.landing-wordmark .a-underline {
  position: relative;
}
.landing-wordmark .a-underline::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.2em;
  width: 0.5em;
  height: 2px;
  transform: translateX(-50%);
  background: var(--red);
  box-shadow: 0 0 6px var(--red-glow);
}

/* ============================================================
   Form controls — shared
   ============================================================ */
input[type="search"],
input[type="text"],
select {
  width: 100%;
  height: 44px;
  border-radius: 0;
  border: none;
  border-bottom: 1px solid var(--ink-line);
  background: transparent;
  color: var(--ink);
  padding: 0 2px 0 2px;
  outline: none;
  font-family: var(--f-serif);
  font-weight: 300;
  font-size: 16px;
  transition: border-color 200ms ease;
}

input[type="search"]::placeholder {
  color: var(--ink-faint);
  font-style: italic;
}

input:focus,
select:focus {
  border-color: var(--red);
}

select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 24px;
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
    linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
  background-position:
    calc(100% - 12px) 19px,
    calc(100% - 6px) 19px;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

select option {
  background: var(--bg-raised);
  color: var(--ink);
  font-family: var(--f-serif);
}

/* ============================================================
   Site-wide footer (script.js injects it on every page)
   Hidden on Explorer + Study where it'd compete with iframes.
   ============================================================ */
.site-footer {
  position: relative;
  z-index: 1;
  margin: 64px auto 36px;
  padding: 18px 24px 0;
  width: min(1200px, 100% - 64px);
  border-top: 1px solid var(--ink-line);
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  align-items: baseline;
  justify-content: space-between;
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: lowercase;
  color: var(--ink-faint);
}
.site-footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 22px;
}
.site-footer a {
  color: var(--ink-faint);
  transition: color 200ms ease;
}
.site-footer a:hover { color: var(--ink); }
.site-footer-mark {
  font-family: var(--f-serif);
  font-style: italic;
  letter-spacing: 0.05em;
  text-transform: none;
}
/* Iframe-style pages skip the footer. The home page does too — its
   centerpiece (the enso) takes the whole viewport via position:fixed
   so a flow-positioned footer would collide with the nav grain. */
.explorer-body .site-footer,
.study-body .site-footer,
.home-body .site-footer { display: none; }
