/* ==========================================================================
   PhotoSensei — photosensei.app
   The Dojo. Dark only. No light mode is defined anywhere in this file.
   Palette is the brand-tokens.json "chrome.dark" set, verbatim.
   ========================================================================== */

/* --- Inter, self-hosted, variable ---------------------------------------- */
/* Inter 4.1, rsms/inter GitHub release. SIL OFL 1.1 — see
   /assets/fonts/Inter-LICENSE-OFL.txt */
@font-face {
  font-family: "InterVariable";
  src: url("../fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens -------------------------------------------------------------- */
:root {
  --ground:      #191612;
  --surface:     #221E18;
  --text:        #ECE7DE;
  --muted:       #A29C90;
  --hairline:    rgba(236, 231, 222, 0.14);
  --accent:      #C2953F;
  --accent-tint: rgba(194, 149, 63, 0.12);

  --font: "InterVariable", system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;

  --wrap: 1080px;
  --prose: 62ch;

  --gap-section: clamp(4.5rem, 11vw, 8.5rem);
  --pad-x: clamp(1.25rem, 5vw, 2.5rem);
}

/* --- Reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font);
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: 1.75;
  color: var(--text);
  background-color: var(--ground);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;

  /* Shoji lattice. Thin lines, very low opacity, taller-than-wide panes.
     Pure CSS — no image file. Scrolls with the page: no parallax. */
  background-image:
    repeating-linear-gradient(to right,
      rgba(236, 231, 222, 0.055) 0 1px, transparent 1px 104px),
    repeating-linear-gradient(to bottom,
      rgba(236, 231, 222, 0.038) 0 1px, transparent 1px 156px);
  background-position: center top;
}

img, svg, picture { display: block; max-width: 100%; }
img { height: auto; }

ul { list-style: none; margin: 0; padding: 0; }

h1, h2, p { margin: 0; }

/* --- Links --------------------------------------------------------------- */
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 0.25em; }
a:hover { color: var(--text); }

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

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.75rem 1.25rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--accent);
  text-decoration: none;
}
.skip:focus { left: 1rem; top: 1rem; }

/* --- Layout -------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}

.prose { max-width: var(--prose); }

.section {
  position: relative;
  padding-block: var(--gap-section);
}

/* Hairline rules between sections — not bordered cards. */
.section + .section::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: var(--pad-x);
  height: 1px;
  background: var(--hairline);
  max-width: var(--wrap);
  margin-inline: auto;
}

/* --- Wordmark ------------------------------------------------------------
   Built as real HTML text on purpose. The wordmark SVGs in the brand kit
   carry a live <text> element with no outlines; loaded through <img> they
   render in a sandboxed document that cannot reach this page's @font-face,
   so they would fall back to the wrong face. Text is the only correct route.
   PHOTO 300 / SENSEI 700, both tracked 0.20em, with one extra tracking step
   of separation between the words.                                        */
.wordmark {
  display: inline-block;
  font-weight: 400;
  letter-spacing: 0.20em;
  line-height: 1;
  white-space: nowrap;
  text-transform: uppercase;
}
.wordmark .wm-photo  { font-weight: 300; color: var(--muted); }
.wordmark .wm-sensei { font-weight: 700; color: var(--text); margin-inline-start: 0.20em; }

/* --- Header -------------------------------------------------------------- */
.site-head {
  position: relative;
  z-index: 2;
  padding-block: clamp(1.5rem, 4vw, 2.25rem);
}
.site-head .wrap {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  text-decoration: none;
  font-size: 0.9375rem;
}
.brand:hover .wm-photo { color: var(--text); }
.brand-mark { width: 30px; height: 30px; flex: none; }

/* --- Type scale ----------------------------------------------------------
   Hierarchy comes from size and space. No heading exceeds weight 500.     */
h1 {
  font-size: clamp(2.75rem, 9.5vw, 5rem);
  font-weight: 200;
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin-block-end: clamp(1.25rem, 3vw, 1.75rem);
}

h2 {
  font-size: clamp(1.6rem, 4.4vw, 2.375rem);
  font-weight: 300;
  line-height: 1.18;
  letter-spacing: -0.015em;
  margin-block-end: clamp(1.5rem, 4vw, 2.25rem);
  max-width: 20ch;
}

.lede {
  font-size: clamp(1.0625rem, 2.6vw, 1.375rem);
  font-weight: 300;
  line-height: 1.6;
  max-width: 34ch;
  color: var(--text);
}

.section p + p { margin-block-start: 1.5rem; }

/* The short second statement in each section. Brass as a line — never a
   flood — is the whole point of this rule. */
.section .aphorism {
  border-inline-start: 1px solid var(--accent);
  padding-inline-start: clamp(1rem, 3vw, 1.5rem);
  color: var(--muted);
  font-weight: 300;
  margin-block-start: clamp(2rem, 5vw, 2.75rem);
}

/* Micro-label: 11px uppercase, wide tracking. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.20em;
  line-height: 1;
  color: var(--accent);
  margin-block-end: clamp(1.5rem, 4vw, 2rem);
}
.eyebrow::after {
  content: "";
  flex: none;
  width: 42px;
  height: 1px;
  background: var(--accent);
}

/* --- Hero ---------------------------------------------------------------- */
.hero { padding-block: clamp(0.5rem, 2vw, 1rem) var(--gap-section); }

/* Left edge shared with the h1 below it — the whole page hangs off one line. */
.hero-art {
  max-width: 720px;
  margin-inline: 0;
  margin-block: clamp(1rem, 4vw, 2.25rem) clamp(3rem, 8vw, 5rem);
}
.hero-art img { width: 100%; }

/* --- Store badges --------------------------------------------------------
   Official artwork, unmodified. The Google asset ships with its mandated
   clear space baked into the canvas (41px of transparent margin around a
   564x168 badge); it is never cropped. Extra CSS clear space is added on
   top of that for both. Apple's badge is rendered at 48px tall — at or
   above the Google badge's 47.7px artwork height, and above Apple's 40px
   minimum.                                                                */
.badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
  margin-block-start: clamp(2rem, 5vw, 2.75rem);
  margin-inline-start: -11.6px; /* cancels the Google asset's baked-in margin */
}
.badges a { display: inline-block; line-height: 0; border-radius: 6px; }
.badges img { display: block; width: auto; }

.badge-play  { height: 71px; }  /* canvas; artwork inside is 47.7px tall */
.badge-apple { height: 48px; margin: 11.6px 6px; }

@media (max-width: 430px) {
  .badges { margin-inline-start: -10.2px; }
  .badge-play  { height: 62px; }
  .badge-apple { height: 42px; margin: 10.2px 5px; }
}

/* --- Ensō watermark ------------------------------------------------------
   photosensei-mark.svg — genuine path artwork, safe through <img>.        */
.has-enso { overflow: hidden; }
.enso {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  user-select: none;
  inset-inline-end: -22%;
  inset-block-start: 8%;
  width: min(115vw, 780px);
  opacity: 0.055;
}
.section > .wrap { position: relative; z-index: 1; }

/* --- Rank hanko chips ----------------------------------------------------
   Drawn the way the app draws rank objects: a rounded square in the rank's
   LOCKED fill, a 1.5px edge INSET into the fill (box-shadow inset, so it
   never grows the box the way a border would), and the Phosphor aperture
   glyph centred at 55% of chip width.

   The glyph is one copied file, assets/img/aperture.svg, used as a CSS mask
   and painted with the chip's own `color`. That is what lets a single file
   serve both the white and the gold glyph. It cannot be an <img>: an SVG
   loaded through <img> renders in a sandboxed document where currentColor
   resolves to the initial colour, not this page's.

   Locked fills only — never displayOnDark, which is for the phone app. The
   chips appear on rank objects and nowhere else: not on chrome, buttons,
   headers or backgrounds. Rank names stay muted and are deliberately not
   colour-matched to the chips.                                            */
.ranks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
  gap: clamp(1.5rem, 4vw, 2.25rem) clamp(0.75rem, 2.5vw, 1.5rem);
  margin-block-start: clamp(2.75rem, 7vw, 4rem);
  max-width: 760px;
}
.rank {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  text-align: center;
}
.chip {
  position: relative;
  display: block;
  width: clamp(46px, 9vw, 62px);
  aspect-ratio: 1;
  border-radius: 16.667%;          /* 8px at a 48px chip, scaled */
  background-color: var(--chip-fill);
  color: #FFFFFF;                  /* glyph colour; overridden per rank */
}

/* 1.5px edge drawn inside the fill, following the same corner radius. */
.chip::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1.5px var(--chip-edge);
}

/* Aperture glyph, 55% of chip width, centred. */
.chip::after {
  content: "";
  position: absolute;
  inset: 22.5%;                    /* leaves the glyph at 55% of the chip */
  background-color: currentColor;
  -webkit-mask: url("../img/aperture.svg") no-repeat center / contain;
          mask: url("../img/aperture.svg") no-repeat center / contain;
}

/* Locked belt values. Fill and edge only — nothing else varies. */
.chip-closed-shutter { --chip-fill: #C9C4BC; --chip-edge: rgba(236, 231, 222, 0.32); }
.chip-open-shutter   { --chip-fill: #2d9e4e; --chip-edge: rgba(236, 231, 222, 0.32); }
.chip-fast-shutter   { --chip-fill: #6B21A8; --chip-edge: rgba(236, 231, 222, 0.32); }
.chip-master-shutter { --chip-fill: #6B3A1F; --chip-edge: rgba(236, 231, 222, 0.32); }
.chip-sensei-eye     { --chip-fill: #1a1a1a; --chip-edge: rgba(255, 215, 0, 0.55); color: #FFD700; }
.chip-shihan         { --chip-fill: #8B0000; --chip-edge: rgba(255, 215, 0, 0.55); color: #FFD700; }
.rank-name {
  font-size: 0.6875rem;
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  /* Two-line box on every rank so the row keeps one baseline whether the
     name wraps or not. */
  min-height: calc(2 * 1.45em);
}

/* --- Screenshot grid -----------------------------------------------------
   Display order equals source order. One block per screenshot: add, remove
   or reorder a <li> and nothing else changes. No per-item classes and no
   width math anywhere. Every cell is a fixed 9:19.5 frame and the image is
   scaled to fit inside it, so a capture at any other aspect ratio drops in
   without breaking the row — leftover space falls on the ink ground.      */
.shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: clamp(0.75rem, 2.5vw, 1.5rem);
}
.shots > li { aspect-ratio: 9 / 19.5; }
.shots img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 4px;
}

@media (min-width: 560px) {
  .shots { grid-template-columns: repeat(auto-fit, minmax(172px, 1fr)); }
}
@media (min-width: 900px) {
  .shots { grid-template-columns: repeat(auto-fit, minmax(192px, 1fr)); }
}

/* --- Buttons -------------------------------------------------------------
   Brass outline over the accent tint. Never a solid brass slab.           */
.btn {
  display: inline-block;
  padding: 0.85rem 1.75rem;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--accent-tint);
  color: var(--text);
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: background-color 0.25s ease, color 0.25s ease;
}
.btn:hover { background: rgba(194, 149, 63, 0.2); color: var(--text); }

/* --- Footer -------------------------------------------------------------- */
.site-foot {
  position: relative;
  padding-block: clamp(3rem, 8vw, 5rem);
  font-size: 0.9375rem;
}
.site-foot::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: var(--pad-x);
  height: 1px;
  background: var(--hairline);
  max-width: var(--wrap);
  margin-inline: auto;
}
.foot-rows {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.foot-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.9rem;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.foot-links .sep { color: var(--muted); }
.foot-links a { color: var(--muted); text-decoration: none; }
.foot-links a:hover { color: var(--accent); text-decoration: underline; }
.foot-note { color: var(--muted); font-size: 0.8125rem; letter-spacing: 0.02em; }

/* --- 404 ----------------------------------------------------------------- */
.notfound {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 58vh;
  padding-block: var(--gap-section);
}
.notfound .numeral {
  font-size: clamp(4.5rem, 18vw, 9rem);
  font-weight: 200;
  line-height: 1;
  letter-spacing: 0.02em;
  color: var(--muted);
  margin-block-end: clamp(1.25rem, 4vw, 2rem);
}
.notfound .btn { margin-block-start: clamp(2rem, 5vw, 2.75rem); }

/* --- Scroll reveal -------------------------------------------------------
   At most a slow fade and a short rise. No parallax, no bounce. Content is
   fully visible when JavaScript is off — the hiding rule is scoped to .js. */
.js:not(.no-reveal) [data-reveal] {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.75s ease, transform 0.75s ease;
  will-change: opacity, transform;
}
.js:not(.no-reveal) [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}
