/* Board glyphs as CSS masks. A glyph is painted in currentColor, so it follows the text in light and dark. */
/* Paste the rules you use into a single-file page; nothing here loads a file. */
:root {
  --board-u: 1.3px; /* px per board unit for essay marks: one scale everywhere */
  --g-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8.3 12H15.7M12 3V21' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --g-mark-small: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 3.5V20.5' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='5' cy='12' r='4.6' fill='currentColor'/%3E%3Ccircle cx='19' cy='12' r='3.6' fill='none' stroke='currentColor' stroke-width='2'/%3E%3C/svg%3E");
  --g-return: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='6' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Cpath d='M16.3 6H20V17H5.5M9 13.4L5.4 17L9 20.6' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-out: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4 9V20H15' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='9.4' cy='14.6' r='3.4' fill='currentColor'/%3E%3Cpath d='M12.6 11.4L20 4M14.2 4H20V9.8' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-break: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 8'%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='36' cy='4' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --g-end: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --g-quote: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8'%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='4' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --g-index: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='4' cy='20' r='3.4' fill='currentColor'/%3E%3Cpath d='M10.5 4H23M10.5 12H23M10.5 20H23' fill='none' stroke='currentColor' stroke-width='1.4' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-link: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M1.5 12H8M16 12H22.5M12 1.5V8M12 16V22.5' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M8 8H16V16H8Z' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-next: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Cpath d='M9.5 12H21M17.2 8.2L21 12L17.2 15.8' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-previous: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='20' cy='12' r='3.4' fill='currentColor'/%3E%3Cpath d='M14.5 12H3M6.8 8.2L3 12L6.8 15.8' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-top: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3Cpath d='M12 14.5V3M8.2 6.8L12 3L15.8 6.8' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M15.5 7.5V3.5H3.5V15.5H7.5' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M8.5 8.5H20.5V20.5H8.5Z' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='14.5' cy='14.5' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --g-feed: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='4' cy='20' r='3.4' fill='currentColor'/%3E%3Cpath d='M4 12H12V20M4 4H20V20' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --g-mode-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Ccircle cx='4' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --g-mode-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --g-mode-system: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath d='M0.8 4H7.2M4 0.8V7.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3C/svg%3E");
  --m-three-three: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M4 12H31.2M4 20H15.7M24.3 20H31.2M4 28H31.2M12 4V31.2M20 4V15.7M20 24.3V31.2M28 4V31.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M4 4H31.2M4 4V31.2' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='20' cy='20' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-fifth-line: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 24'%3E%3Cpath d='M0.8 4H36M8.3 12H36M0.8 20H7.7M16.3 20H36M4 0.8V7.7M4 16.3V23.2M12 0.8V15.7M20 0.8V23.2M28 0.8V23.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M36 0.8V23.2' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-prisoner: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M0.8 4H7.7M16.3 4H23.2M0.8 20H7.7M16.3 20H23.2M4 0.8V7.7M4 16.3V23.2M20 0.8V7.7M20 16.3V23.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='20' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='12' cy='20' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='4' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-atari: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M0.8 4H7.7M16.3 4H23.2M16.3 12H23.2M0.8 20H7.7M16.3 20H23.2M4 0.8V7.7M4 16.3V23.2M20 0.8V23.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='4' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='12' cy='20' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-ladder: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Ccircle cx='4' cy='28' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='28' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='20' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='12' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='20' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='12' cy='36' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='20' cy='28' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='28' cy='20' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='36' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-net: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M0.8 4H15.7M16.3 12H23.2M0.8 20H7.7M16.3 20H23.2M4 0.8V7.7M4 16.3V23.2M12 0.8V7.7M20 8.3V23.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-ko: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 24'%3E%3Cpath d='M17.4 12H22.6M20 9.4V14.6' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='12' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='28' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='20' cy='20' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='12' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-seki: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M8.3 12H15.7M12 4V15.7' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M8.3 4H15.7' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='28' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='20' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='20' cy='20' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='4' cy='28' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='12' cy='28' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-two-eyes: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 24'%3E%3Cpath d='M9.4 12H14.6M12 9.4V14.6M25.4 12H30.6M28 9.4V14.6' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='36' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='36' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='36' cy='20' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-ponnuki: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9.4 12H14.6M12 9.4V14.6' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='12' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-tigers-mouth: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 16'%3E%3Cpath d='M8.3 12H15.7M12 8.3V15.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='12' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='12' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-bamboo-joint: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 24'%3E%3Cpath d='M0.8 12H15.2M4 8.3V15.7M12 8.3V15.7' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-empty-triangle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.4 4H14.6M12 1.4V6.6' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-crosscut: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='4' cy='12' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-wedge: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 35 19'%3E%3Cpath d='M9.5 1.5V5.2M9.5 13.8V17.5M17.5 1.5V5.2M17.5 13.8V17.5M25.5 1.5V5.2M25.5 13.8V17.5' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='17.5' cy='9.5' r='3.4' fill='currentColor'/%3E%3Ccircle cx='9.5' cy='9.5' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='25.5' cy='9.5' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-one-point-jump: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 32'%3E%3Cpath d='M8 12.3V19.7M5.4 16H10.6M8 13.4V18.6' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='8' cy='8' r='3.4' fill='currentColor'/%3E%3Ccircle cx='8' cy='24' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-knights-move: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 16'%3E%3Cpath d='M0.8 4H15.7M8.3 12H23.2M4 0.8V7.7M12 0.8V15.2M20 8.3V15.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='4' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-wall: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M8.3 4H31.2M8.3 12H31.2M8.3 20H31.2M8.3 28H31.2M12 0.8V31.2M20 0.8V31.2M28 0.8V31.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='28' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-crawl: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 24'%3E%3Cpath d='M4 16.3V20M12 16.3V20M20 16.3V20M28 16.3V20' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M0.8 20H31.2' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='4' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='12' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='20' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3Ccircle cx='28' cy='4' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-territory: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M4 12H23.7M4 20H23.7M12 4V23.7M20 4V23.7' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M4 4H23.7M4 4V23.7' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='28' cy='4' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='20' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='28' r='3.4' fill='currentColor'/%3E%3Ccircle cx='20' cy='28' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='28' r='3.4' fill='currentColor'/%3E%3Ccircle cx='4' cy='28' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-liberties: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4 12H7.7M16.3 12H20M12 4V7.7M12 16.3V20' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='12' cy='4' r='1.7' fill='currentColor'/%3E%3Ccircle cx='4' cy='12' r='1.7' fill='currentColor'/%3E%3Ccircle cx='20' cy='12' r='1.7' fill='currentColor'/%3E%3Ccircle cx='12' cy='20' r='1.7' fill='currentColor'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-handicap: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M4 12H7.7M16.3 12H23.7M32.3 12H36M4 20H36M4 28H7.7M16.3 28H23.7M32.3 28H36M12 4V7.7M12 16.3V23.7M12 32.3V36M20 4V36M28 4V7.7M28 16.3V23.7M28 32.3V36' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M4 4H36M4 36H36M4 4V36M36 4V36' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='12' r='3.4' fill='currentColor'/%3E%3Ccircle cx='12' cy='28' r='3.4' fill='currentColor'/%3E%3Ccircle cx='28' cy='28' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-tengen: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M0.8 4H39.2M0.8 12H39.2M0.8 20H15.7M24.3 20H39.2M0.8 28H39.2M0.8 36H39.2M4 0.8V39.2M12 0.8V39.2M20 0.8V15.7M20 24.3V39.2M28 0.8V39.2M36 0.8V39.2' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='20' cy='20' r='3.4' fill='currentColor'/%3E%3C/svg%3E");
  --m-speaks-back: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M0 8H11.7M20.3 8H24M0 16H3.7M12.3 16H24M8 0V11.7M8 20.3V24M16 0V3.7M16 12.3V24' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='8' cy='16' r='3.4' fill='currentColor'/%3E%3Ccircle cx='16' cy='8' r='2.75' fill='none' stroke='currentColor' stroke-width='1.3'/%3E%3C/svg%3E");
  --m-world: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M4 12H36M4 20H36M4 28H36M12 4V36M20 4V36M28 4V36' fill='none' stroke='currentColor' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Cpath d='M4 4H36M4 36H36M4 4V36M36 4V36' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='butt' stroke-linejoin='miter'/%3E%3Ccircle cx='20' cy='20' r='1.7' fill='currentColor'/%3E%3C/svg%3E");
}
.g, .m { display: inline-block; flex-shrink: 0; background: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat; mask: var(--glyph) center / contain no-repeat; }
.g { width: 1em; height: 1em; vertical-align: -0.17em; }
.m { width: calc(var(--w) * var(--board-u)); height: calc(var(--h) * var(--board-u)); }

/* working glyphs, with the sizes they take in running text */
.g-mark { --glyph: var(--g-mark); width: 1.35em; height: 1.35em; vertical-align: -0.34em; }
.g-mark-small { --glyph: var(--g-mark-small); width: 1.35em; height: 1.35em; vertical-align: -0.34em; }
.g-return { --glyph: var(--g-return); width: 0.98em; height: 0.98em; vertical-align: -0.2em; margin-left: 0.3em; }
.g-out { --glyph: var(--g-out); width: 0.86em; height: 0.86em; vertical-align: -0.08em; margin: 0 0.1em 0 0.22em; }
.g-break { --glyph: var(--g-break); display: block; width: 60px; height: 12px; margin: 2.75rem auto 2.5rem; }
.g-end { --glyph: var(--g-end); width: 0.62em; height: 0.62em; vertical-align: 0.01em; margin-left: 0.4em; }
.g-quote { --glyph: var(--g-quote); width: 0.8em; height: 0.4em; vertical-align: 0.42em; }
.g-index { --glyph: var(--g-index); }
.g-link { --glyph: var(--g-link); }
.g-next { --glyph: var(--g-next); }
.g-previous { --glyph: var(--g-previous); }
.g-top { --glyph: var(--g-top); }
.g-copy { --glyph: var(--g-copy); }
.g-feed { --glyph: var(--g-feed); }
.g-mode-light { --glyph: var(--g-mode-light); width: 1.3em; height: 1.3em; vertical-align: -0.3em; }
.g-mode-dark { --glyph: var(--g-mode-dark); width: 1.3em; height: 1.3em; vertical-align: -0.3em; }
.g-mode-system { --glyph: var(--g-mode-system); width: 1.3em; height: 1.3em; vertical-align: -0.3em; }

/* essay marks: --w and --h are the mark's size in board units */
.m-three-three { --glyph: var(--m-three-three); --w: 32; --h: 32; }
.m-fifth-line { --glyph: var(--m-fifth-line); --w: 40; --h: 24; }
.m-prisoner { --glyph: var(--m-prisoner); --w: 24; --h: 24; }
.m-atari { --glyph: var(--m-atari); --w: 24; --h: 24; }
.m-ladder { --glyph: var(--m-ladder); --w: 40; --h: 40; }
.m-net { --glyph: var(--m-net); --w: 24; --h: 24; }
.m-ko { --glyph: var(--m-ko); --w: 32; --h: 24; }
.m-seki { --glyph: var(--m-seki); --w: 32; --h: 32; }
.m-two-eyes { --glyph: var(--m-two-eyes); --w: 40; --h: 24; }
.m-ponnuki { --glyph: var(--m-ponnuki); --w: 24; --h: 24; }
.m-tigers-mouth { --glyph: var(--m-tigers-mouth); --w: 24; --h: 16; }
.m-bamboo-joint { --glyph: var(--m-bamboo-joint); --w: 16; --h: 24; }
.m-empty-triangle { --glyph: var(--m-empty-triangle); --w: 16; --h: 16; }
.m-crosscut { --glyph: var(--m-crosscut); --w: 16; --h: 16; }
.m-wedge { --glyph: var(--m-wedge); --w: 35; --h: 19; }
.m-one-point-jump { --glyph: var(--m-one-point-jump); --w: 16; --h: 32; }
.m-knights-move { --glyph: var(--m-knights-move); --w: 24; --h: 16; }
.m-wall { --glyph: var(--m-wall); --w: 32; --h: 32; }
.m-crawl { --glyph: var(--m-crawl); --w: 32; --h: 24; }
.m-territory { --glyph: var(--m-territory); --w: 32; --h: 32; }
.m-liberties { --glyph: var(--m-liberties); --w: 24; --h: 24; }
.m-handicap { --glyph: var(--m-handicap); --w: 40; --h: 40; }
.m-tengen { --glyph: var(--m-tengen); --w: 40; --h: 40; }
.m-speaks-back { --glyph: var(--m-speaks-back); --w: 24; --h: 24; }
.m-world { --glyph: var(--m-world); --w: 40; --h: 40; }

/* ==========================================================================
   Slow Fast — essay reading surface
   --------------------------------------------------------------------------
   Ported from the visual reference at
   .config/slowfast-workspace/essay-reference.html (style block, lines 11-173).

   Rules marked LIFTED are byte-for-byte the reference's intent and must not
   drift. Rules marked NORMALISED and INVENTED are deliberate departures and
   are each recorded in docs/design/essay-reading-surface.md so the fidelity
   gate can exempt them.

   No JavaScript is served with this stylesheet. There is no theme toggle.
   Font delivery is SELF-HOSTED, and this file is the only place it is
   declared. The page shell emits no third-party font link and no preconnect,
   so an essay page reaches exactly one origin and no reader is disclosed to a
   font CDN in order to see type. See section 0.
   ========================================================================== */


/* --------------------------------------------------------------------------
   0. FONTS
   Newsreader, self-hosted, OFL-1.1. Two files and no more: it is a variable
   font, so one roman binary carries every weight and optical size the site
   asks for and one italic binary carries the same. Static instances would be
   a dozen files to say less.

   `font-weight: 400 500` is the range this surface actually uses — body text
   at 400, `h2` and `strong` at 500 — declared as a range so the browser
   interpolates rather than synthesising. The binaries carry the wider 200-800
   axis upstream ships; declaring the narrower range here is what keeps a
   stray `font-weight: 700` from rendering as something this design never
   chose. The `opsz` 6-72 axis is what `body { font-optical-sizing: auto }`
   drives, and it is the reason the same outlines hold up at the 3rem h1 and
   at the .86rem notes.

   `font-display: swap` is deliberate and matches the motion budget: the
   fallback stack paints immediately and the webfont swaps in when it lands.
   Nothing on this surface is allowed to withhold legible text.

   Provenance, pinned upstream refs and the reason the axis was NOT narrowed
   are in `fonts/PROVENANCE.md`. The licence travels with the binaries: the
   build copies `fonts/OFL.txt` into `dist/essays/assets/fonts/`.
   INVENTED — the reference linked Google Fonts; this surface does not.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("/assets/fonts/newsreader-variable-roman.woff2") format("woff2");
}

@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url("/assets/fonts/newsreader-variable-italic.woff2") format("woff2");
}


/* --------------------------------------------------------------------------
   1. TOKENS
   Three blocks, in this exact order: bare :root, the prefers-color-scheme
   query guarded by :root:not([data-theme="light"]), then :root[data-theme="dark"].
   All six colours are redefined in full in each of the two dark blocks so
   that no colour has its only definition inside a media query.
   LIFTED.

   A seventh token, --still, is not a colour. It carries the correction every
   photograph on the site is printed through, so that the two dark blocks can
   set it the same way they set the colours and no rule further down has to be
   written twice. See THE STILLS in section 9.

   `data-theme` is set by the control in the masthead and by nothing else. It
   is absent until a reader chooses, and choosing System removes it again, so
   the default on every first visit is still the operating system's answer.
   -------------------------------------------------------------------------- */

:root {
  --paper: #fcfcfa;
  --ink: #1b1d24;
  --ink-2: #6a6d78;
  --rule: #dcdcd6;
  --evidence: #2a58b4;
  --plate: #eeeeea;
  --still: none;
  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #121316;
    --ink: #e6e4de;
    --ink-2: #9295a0;
    --rule: #2c2e35;
    --evidence: #88a9ec;
    --plate: #1d1f25;
    --still: contrast(.92) brightness(.88);
  }
}

/* THE EXPLICIT CHOICE, either way round.
   The guard on the media query above lets data-theme="light" win over an OS
   dark preference; the block below lets data-theme="dark" win over an OS light
   preference. `color-scheme` is narrowed from `light dark` to the one chosen
   in each, so the scrollbars, the form controls and the canvas the browser
   paints behind the page agree with the tokens instead of following the OS on
   their own. */
:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  --paper: #121316;
  --ink: #e6e4de;
  --ink-2: #9295a0;
  --rule: #2c2e35;
  --evidence: #88a9ec;
  --plate: #1d1f25;
  --still: contrast(.92) brightness(.88);
  color-scheme: dark;
}


/* --------------------------------------------------------------------------
   2. RESET AND BODY
   LIFTED.
   -------------------------------------------------------------------------- */

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

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Newsreader", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  font-optical-sizing: auto;
  font-size: 1.0625rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-feature-settings: "kern", "liga";
}

@media (min-width: 40em) {
  body {
    font-size: 1.1875rem;
    line-height: 1.5;
  }
}

i,
em {
  font-style: italic;
}

b,
strong {
  font-weight: 500;
}


/* --------------------------------------------------------------------------
   3. LAYOUT
   The measure is 38rem on essay pages and on the index alike — one measure is
   what makes them read as one publication rather than a feed bolted to a
   magazine.

   --gutter mirrors the horizontal half of .page's padding so the wide-figure
   breakout in section 9 and the page padding can never fall out of agreement:
   change one number here and both follow.

   overflow-x: clip contains the wide-figure breakout. clip is used rather than
   hidden precisely because it does not create a scroll container, so
   scroll-behavior and :target scrolling into the notes still work.
   .page padding and max-width are LIFTED; --gutter and overflow-x are INVENTED.
   -------------------------------------------------------------------------- */

.page {
  --gutter: 1.25rem;
  max-width: 33rem;
  margin: 0 auto;
  padding: 3.5rem 1.25rem 6rem;
  overflow-x: clip;
}

@media (min-width: 40em) {
  .page {
    --gutter: 3.25rem;
    padding: 6.5rem 3.25rem 7rem;
  }
}


/* --------------------------------------------------------------------------
   4. MASTHEAD
   INVENTED — the reference has no masthead at all.
   Sits outside <header> as the first child of .page. Carries its own
   text-indent: 0 rather than relying on the "header p" exception, because it
   is not inside header and may contain a paragraph.
   On an essay page it carries the bare "Slow Fast" wordmark. On the index it
   also carries the stance line beneath that wordmark.
   -------------------------------------------------------------------------- */

.masthead {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  /* A link and a glyph fit beside the wordmark at every width this site is
     read at. The wrap stays as the floor: if a longer word ever joins them,
     the right-hand side drops to a line of its own rather than crushing the
     wordmark. */
  row-gap: .9rem;
  font-size: .86rem;
  letter-spacing: .02em;
  color: var(--ink-2);
  text-indent: 0;
  margin: 0 0 3.2rem;
}

.masthead a {
  color: inherit;
  text-decoration: none;
}

.masthead .wordmark {
  color: var(--ink);
}

/* The stance line is content, not chrome, and renders on the index only. */
.masthead .stance {
  display: block;
  margin: .55rem 0 0;
  color: var(--ink-2);
  text-indent: 0;
  font-style: italic;
}

/* THE MASTHEAD'S RIGHT-HAND SIDE.
   `.masthead` is space-between and was always built for two items. Without a
   second one the about page is published and unreachable from every reading
   page. It inherits the masthead's own quiet scale — this is a way out, not a
   thing to look at. */
.masthead-about {
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
}

@media (prefers-reduced-motion: no-preference) {
  .masthead-about {
    transition: color 120ms ease;
  }
}

.masthead-about:hover,
.masthead-about:focus-visible {
  color: var(--ink);
}

/* THE COLOUR MODE CONTROL.
   One button in the top right corner of the masthead, drawn as a single point
   of the board in each of the three states a point can be in: white played,
   black played, nothing played. Light, dark, and whatever the device says. A
   press moves to the next one. The drawing lives in section 15 with the rest
   of the glyphs and is selected there by the root element's own state.

   IT IS ONE GLYPH WIDE ON PURPOSE. The three words this replaced could not
   share the wordmark's line beside the stance line and dropped to a third row
   of their own, which put a widget under the publication's own sentence.

   HIDDEN UNTIL IT CAN WORK. `data-js` is set by the inline script in the head,
   so a reader with scripting off never sees a button that would do nothing.
   The operating system's preference is still the answer for them, and it is
   still the answer for everyone who never touches this. */
.theme-toggle {
  display: none;
}

:root[data-js="on"] .theme-toggle {
  display: inline-block;
  line-height: 1;
  /* The padding is the hit area. It matches the About link's own line box
     rather than growing the control into something the masthead has to make
     room for. */
  padding: .1rem .16rem;
  color: var(--ink-2);
  background: none;
  border: 0;
  border-radius: 2px;
  cursor: pointer;
}

@media (prefers-reduced-motion: no-preference) {
  .theme-toggle {
    transition: color 120ms ease;
  }
}

.theme-toggle:hover,
.theme-toggle:focus-visible {
  color: var(--ink);
}

/* The masthead is space-between and was built for two items. The control and
   the way out travel together as the second one. */
.masthead-tools {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  white-space: nowrap;
  /* Right-aligned whether it shares the wordmark's line or takes its own:
     space-between only aligns it on the line they share. */
  margin-left: auto;
}

/* THE SITE FOOTER. Index only. The standalone pages would otherwise be
   reachable from the about page and from each other, but never from the front
   door. Same quiet scale as the masthead; the rule above it does the
   separating, exactly as `.notes` does on an essay. */
.site-footer {
  margin-top: 4.5rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem;
  font-size: .86rem;
  letter-spacing: .02em;
  color: var(--ink-2);
  text-indent: 0;
}

.site-footer a {
  color: inherit;
  text-decoration: none;
}

@media (prefers-reduced-motion: no-preference) {
  .site-footer a {
    transition: color 120ms ease;
  }
}

.site-footer a:hover,
.site-footer a:focus-visible {
  color: var(--ink);
}


/* On the index the publication's name is the page's heading, so it is an
   <h1>. It must not inherit the essay-title type scale — visually it stays
   exactly the masthead wordmark it was before. */
.masthead h1.wordmark-heading {
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  margin: 0;
}


/* --------------------------------------------------------------------------
   5. HEADER, TITLE AND BYLINE
   NORMALISED: the reference's single fluid h1 size becomes three fixed
   breakpoint sizes, and its negative tracking is dropped altogether rather
   than written out as a zero value.
   Everything else LIFTED.
   -------------------------------------------------------------------------- */

header h1 {
  font-weight: 400;
  font-size: 2.05rem;
  line-height: 1.06;
  margin: 0 0 1.4rem;
  text-wrap: pretty;
}

@media (min-width: 40em) {
  header h1 {
    font-size: 2.5rem;
  }
}

@media (min-width: 52em) {
  header h1 {
    font-size: 3rem;
  }
}

header .byline {
  font-style: italic;
  font-size: 1.0625rem;
  color: var(--ink-2);
  margin: 0 0 4.25rem;
}

/* The <time> element is emitted inside the fused byline string so the byline
   renders pixel-identically while becoming machine-readable. */
header .byline time {
  font-style: inherit;
  color: inherit;
}

/* INVENTED. A header image sits below the title and byline. The generator adds
   this class to <header> rather than relying on :has(), and it pulls the
   byline's generous 4.25rem trailing space in to 1.6rem so the image reads as
   part of the opening rather than as the first thing in the prose. */
header.has-header-image .byline {
  margin-bottom: 1.6rem;
}


/* --------------------------------------------------------------------------
   6. PROSE
   NORMALISED — this is the one substantive departure from the reference's
   setting of the page.

   The reference sets prose the way a book does: p { margin: 0 } with a 1.55em
   first-line indent, so paragraphs are separated by the indent alone and the
   column runs as an unbroken block of type. That works on a printed page and
   it works for the reference essay, which is broken up by seven numbered
   sections. It does not hold for an essay with no sections at all — several
   here have none — where it renders as one undifferentiated wall.

   So: space between paragraphs, and NO first-line indent. The two are
   alternatives, not additions; using both is a redundancy that reads as an
   error. Indentation is still never stored in the content model — a paragraph
   is <p> and nothing more — the rule set simply separates them differently.

   1.15em is a little under one line (line-height is 1.58), which separates
   clearly without letting the column drift apart.
   -------------------------------------------------------------------------- */

p {
  margin: 0 0 1.15em;
  hyphens: auto;
  /* Never hyphenate a word shorter than 6 characters, and never leave fewer
     than 3 before or after the break. Unconstrained `hyphens: auto` on a
     ragged-right column breaks two-syllable words and litters the right edge
     with hyphens that buy nothing. */
  hyphenate-limit-chars: 6 3 3;
  /* Costs the last lines so a paragraph does not end on a single short word.
     The reference applies this to display type only; body text is where a
     runt is actually distracting. */
  text-wrap: pretty;
}

p:last-child {
  margin-bottom: 0;
}

p.close {
  margin-top: 1.55em;
}

h2 {
  font-weight: 500;
  font-size: 1.42rem;
  line-height: 1.22;
  margin: 3.6rem 0 1.15rem;
  text-wrap: pretty;
  /* Every numbered heading carries an id, so it is a deep-link target. Without
     this it lands flush against the top of the viewport with no air above it.
     The notes list already does the same. */
  scroll-margin-top: 1.5rem;
}

/* The section number. Ordinals are computed at render from document order and
   never stored. */
h2 .n {
  font-weight: 400;
  color: var(--ink-2);
  margin-right: .55em;
}

/* INVENTED. Prose links get the same treatment as note links so that a link
   mark in a body paragraph never falls back to the browser default. Neither
   essay in this slice contains one, so this rule changes nothing today. */
article a {
  color: var(--evidence);
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: .14em;
}

/* Footnote references. LIFTED. */
sup.ref {
  font-size: .6em;
  line-height: 0;
  vertical-align: .62em;
  margin-left: .06em;
}

sup.ref a {
  color: var(--evidence);
  text-decoration: none;
  padding: 0 .12em;
}

sup.ref a:hover {
  text-decoration: underline;
}

/* LIFTED. */
a:focus-visible {
  outline: 2px solid var(--evidence);
  outline-offset: 2px;
  border-radius: 2px;
}


/* --------------------------------------------------------------------------
   7. PULL QUOTES
   LIFTED. Note that .pull declares NO font-weight — it inherits the body's,
   and that is the reference's intent, not an omission. Its negative tracking
   is dropped per the same normalisation as the h1.
   Whether a pull quote carries aria-hidden is computed at render from whether
   its text already occurs in the prose; it is never a styling concern.
   -------------------------------------------------------------------------- */

.pull {
  margin: 2.4rem 0;
  padding: 0 1.5rem 0 0;
  font-size: 1.42rem;
  line-height: 1.28;
  text-indent: 0;
  text-wrap: pretty;
}


/* --------------------------------------------------------------------------
   8. SECTION BREAKS
   INVENTED — the reference has no section breaks. An EMPTY element carrying
   role="separator", which is what announces the break.
   It used to carry the author's literal em dash as text. It now carries the
   `break` glyph — black, white, black — and the box that paints it is in
   section 15 with the rest of the glyphs. What is left here is what the
   element is regardless of what is drawn on it.
   -------------------------------------------------------------------------- */

p.brk {
  text-indent: 0;
  text-align: center;
  color: var(--ink-2);
}


/* --------------------------------------------------------------------------
   9. FIGURES
   INVENTED — the reference has no figures.

   layout: "column" is the 38rem measure and needs no breakout.
   layout: "wide" breaks out WITHOUT shifting the box. The usual half-width
   translate trick is deliberately not used: it would create a stacking
   context, risks fractional-pixel blur on the image, and interferes with
   :target scrolling. Instead a negative margin-inline pulls the figure out to
   half of the wider measure, bounded by the viewport minus one gutter, so it
   never touches the screen edge and never overflows .page's clip.

   A figure whose status is "pending" carries no <img> at all — it renders as a
   caption alone, not as an empty frame.
   -------------------------------------------------------------------------- */

figure {
  margin: 2.8rem 0;
}

figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 2px;
  filter: var(--still);
}

figure figcaption {
  font-size: .86rem;
  line-height: 1.5;
  color: var(--ink-2);
  text-indent: 0;
  margin-top: .7rem;
}

figure figcaption .credit {
  display: block;
  margin-top: .3rem;
}

figure.wide {
  margin-inline: calc(50% - min(26rem, 50vw - var(--gutter)));
}

/* The header image, below title and byline. */
figure.header {
  margin: 0 0 3.4rem;
}

/* THE STILLS.
   Every frame on this site is graded toward the light paper before it is
   committed, which is the right print for #fcfcfa and too bright for #121316:
   a 2001 desert at a mean of 152 glares off a dark page, and its highlights
   come out brighter than the brightest word on it. --still prints them through
   one correction that lifts the blacks a little before it dims the whites, so
   a dark frame keeps its shadows off the paper instead of sinking into it. In
   light mode the token is `none` and the browser skips the filter entirely.

   The value is a printing decision and not a treatment: it is applied on the
   way to the screen, the committed file is untouched, and no frame is altered
   in the sense the colophon means. */

/* THE DIAGRAMS.
   A vendored SVG is drawn INTO the page by the renderer rather than pointed at
   with <img>, because an <img> renders its SVG in a document of its own that
   cannot see this one's data-theme. The shapes therefore read --ink, --paper
   and --evidence off :root here, and follow the reader's choice exactly as the
   prose does. These rules are the style block the vendored files used to carry
   themselves, minus the token definitions they used to carry with it.
   Change one and change `scripts/essays/vendor-figure.mjs`, which is what puts
   the class names on the shapes. */

figure svg.sf {
  display: block;
  width: 100%;
  height: auto;
}

.sf text {
  font-family: 'Newsreader', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
}

.sf .t { fill: var(--ink); font-size: 15px; }
.sf .l { fill: var(--ink-2); font-size: 13.5px; font-style: italic; }
.sf .v { fill: var(--ink); font-size: 14.5px; font-variant-numeric: tabular-nums; }
.sf .ve { fill: var(--evidence); font-size: 14.5px; font-variant-numeric: tabular-nums; }

/* A label that crosses a bar reads through a halo of the paper it sits on. */
.sf .h {
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 5px;
  stroke-linejoin: round;
}

.sf .m,
.sf .m2 {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 10.4px;
}

.sf .m { fill: var(--ink); }
.sf .m2 { fill: var(--ink-2); }

.sf .rule { stroke: var(--rule); stroke-width: 1; fill: none; }
.sf .line { stroke: var(--ink-2); stroke-width: 1; fill: none; }
.sf .ink { fill: var(--ink); }
.sf .ink2 { fill: var(--ink-2); }
.sf .ev { fill: var(--evidence); }
.sf .plate { fill: var(--plate); stroke: var(--rule); stroke-width: 1; }
.sf .open { fill: var(--paper); stroke: var(--ink-2); stroke-width: 1.4; }
.sf .dash { fill: none; stroke: var(--ink-2); stroke-width: 1.2; stroke-dasharray: 4 3; }
.sf .evline { stroke: var(--evidence); stroke-width: 1.4; fill: none; }


/* --------------------------------------------------------------------------
   10. NOTES AND THANKS
   The notes section lives inside <main> but outside </article>.
   LIFTED in full, including the plate bleed on :target — the box-shadow spreads
   the plate .55rem past the list item's own box so a targeted note reads as a
   physical plate laid on the paper rather than as a highlighted rectangle.
   -------------------------------------------------------------------------- */

.notes {
  margin-top: 5.5rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--rule);
  font-size: .86rem;
  line-height: 1.5;
}

@media (min-width: 40em) {
  .notes {
    font-size: .92rem;
  }
}

.notes h2 {
  font-size: 1rem;
  font-weight: 500;
  margin: 0 0 1.1rem;
}

.notes ol {
  list-style: none;
  margin: 0;
  padding: 0;
}

.notes li {
  position: relative;
  padding-left: 2.1rem;
  margin: 0 0 .8rem;
  scroll-margin-top: 1.5rem;
}

.notes li .back {
  position: absolute;
  left: 0;
  top: 0;
  width: 1.7rem;
  color: var(--evidence);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}

/* THE PLATE BLEED. */
.notes li:target {
  background: var(--plate);
  box-shadow: 0 0 0 .55rem var(--plate);
  border-radius: 2px;
}

.notes a {
  color: var(--evidence);
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: .14em;
}

.notes .back:hover {
  text-decoration: underline;
}

/* THE ACKNOWLEDGEMENTS. LIFTED from the reference's `.thanks` block, with the
   colours taken from the tokens rather than repeated as hex. It follows the
   notes and is set smaller and greyer than the prose, because it is the
   author speaking about the essay rather than in it. `text-indent: 0`
   because the first-line indent belongs to the article's flow and this
   paragraph stands outside it. */

.thanks {
  margin-top: 2.6rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--rule);
  font-size: .92rem;
  line-height: 1.5;
  color: var(--ink-2);
}

.thanks p {
  margin: 0;
  text-indent: 0;
}

.thanks a {
  color: var(--evidence);
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: .14em;
}


/* --------------------------------------------------------------------------
   11. ESSAY NAV
   INVENTED. Previous / All essays / next, at the foot of an essay page,
   inside <main> and after the notes. Hidden in print.
   -------------------------------------------------------------------------- */

nav.essay-nav {
  margin-top: 4.5rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
  gap: 1.2rem;
  font-size: .92rem;
  color: var(--ink-2);
}

nav.essay-nav a {
  color: var(--ink-2);
  text-decoration: none;
}

nav.essay-nav a:hover {
  text-decoration: underline;
}

/* Stands in for an absent neighbour so "All essays" keeps its place.
   It must NOT grow: `justify-content: space-between` is what does the
   positioning, and a growing spacer would absorb the free space and leave
   nothing to distribute, packing the two real links together. */
nav.essay-nav .spacer {
  flex: 0 0 auto;
}


/* --------------------------------------------------------------------------
   12. INDEX PAGE
   INVENTED. Same 38rem measure as an essay page. Items are 2.8rem apart with
   no rules between them — the space does the separating.
   -------------------------------------------------------------------------- */

.essay-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.essay-list li {
  margin: 0 0 2.8rem;
}

.essay-list li:last-child {
  margin-bottom: 0;
}

.essay-list .title {
  font-size: 1.42rem;
  font-weight: 500;
  line-height: 1.22;
  margin: 0;
  text-wrap: pretty;
}

.essay-list .title a {
  color: var(--ink);
  text-decoration: none;
}

.essay-list .date {
  display: block;
  font-size: .86rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  margin-top: .45rem;
}

.essay-list .excerpt {
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--ink-2);
  text-indent: 0;
  margin-top: .55rem;
}

/* The most recent essay is featured with a larger title, echoing the first two
   h1 breakpoints so the index and an essay page share one type scale. */
.essay-list li.featured {
  margin-bottom: 3.6rem;
}

.essay-list li.featured .title {
  font-size: 2.05rem;
  font-weight: 400;
  line-height: 1.1;
}

@media (min-width: 40em) {
  .essay-list li.featured .title {
    font-size: 2.5rem;
  }
}

/* THE COVER, on the most recent essay only: the same colour-matched still the
   essay page sets, at the same measure, above the title. It is the one image
   on this page given room to be an image. */
.essay-list .cover {
  display: block;
  margin: 0 0 1.4rem;
}

.essay-list .cover img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--plate);
  filter: var(--still);
}

/* THE THUMBNAILS, on every other essay: the same frame, small, beside the
   title. Flex rather than grid so the text block sets its own height and the
   image simply sits at the top of it.

   One box for all of them. The stills arrive at whatever ratio their film was
   shot in, and a column where each thumbnail is a different height reads as an
   accident rather than a list. The box is set to the ratio most of the frames
   already are and the odd one is covered into it, centred, so the run down the
   page is even. The essay page still sets the full frame uncropped; this crop
   is a layout decision and lives only here. The cover on the featured entry is
   deliberately exempt.

   The ratio is repeated as THUMB_RATIO in render/index-page.mjs, which writes
   it into each img's width and height so the reserved box matches the painted
   one. Change one and change the other. */
.essay-list li.has-thumb {
  display: flex;
  align-items: flex-start;
  gap: 1.1rem;
}

.essay-list .thumb {
  display: block;
  flex: 0 0 6.5rem;
  margin-top: .3rem;
}

.essay-list .thumb img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 2.35;
  object-fit: cover;
  object-position: center;
  background: var(--plate);
  filter: var(--still);
}

.essay-list .entry {
  min-width: 0;
}

@media (min-width: 40em) {
  .essay-list li.has-thumb {
    gap: 1.5rem;
  }

  .essay-list .thumb {
    flex: 0 0 9rem;
  }
}


/* --------------------------------------------------------------------------
   13. SKIP LINK
   INVENTED. Visually hidden until focused, then pinned top-left on the plate.
   Clipped rather than display:none so it stays in the focus order.
   -------------------------------------------------------------------------- */

.skip-link {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link:focus,
.skip-link:focus-visible {
  position: fixed;
  top: .75rem;
  left: .75rem;
  z-index: 10;
  width: auto;
  height: auto;
  margin: 0;
  padding: .55rem .9rem;
  overflow: visible;
  clip-path: none;
  background: var(--plate);
  color: var(--ink);
  border-radius: 2px;
  font-size: .92rem;
  text-decoration: none;
}


/* --------------------------------------------------------------------------
   14. MOTION
   The motion budget is one transition and the reference's already-gated
   scroll-behavior. There is deliberately NO entrance fade anywhere: it would
   delay first legible text, which is the one thing this surface exists to
   deliver. No keyframes are declared in this file.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .essay-list a {
    transition: color 120ms ease, opacity 120ms ease;
  }
}

.essay-list a:hover,
.essay-list a:focus-visible {
  color: var(--evidence);
}


/* --------------------------------------------------------------------------
   15. GLYPHS
   INVENTED — the reference has none.

   The glyphs are a set drawn for this site in the notation of a Go record:
   three primitives, one scale, everything in `currentColor`. They arrive as
   custom properties from `src/assets/glyphs/board.css`, which is GENERATED by
   `glyphs/source/board.py` and concatenated ahead of this file into the one
   hashed stylesheet. Nothing in this section may hand-edit that file; a
   regeneration would silently revert it.

   EVERY GLYPH HERE IS A MASK OVER currentColor. That is the whole reason this
   approach was taken over an <img> or an inline <svg>: a mask cuts the drawing
   out of the text's own colour, so a glyph is never a colour of its own and
   follows light and dark with no second definition and no second asset. The
   SVG travels inside the stylesheet as a `data:` URI, so no glyph costs a
   request and none of them reaches a second origin.

   THE WORKING GLYPHS ARE PSEUDO-ELEMENTS, so no essay's markup changed to get
   one. The essay MARK is real markup — `<span class="m m-tengen">` — because
   it is a per-document choice and content has to be able to name it.

   DECORATIVE, THEREFORE SILENT, WITH ONE EXCEPTION. A pseudo-element is not
   in the accessibility tree and the marks carry aria-hidden, and every one of
   them repeats something the page already says in words. The exception is the
   colour mode point, which IS the content of a control; the pack's rule is
   that such a glyph needs words of its own, and its button carries them in
   `aria-label` and `title`.

   SIX OF THE PACK'S SEVENTEEN WORKING GLYPHS ARE DELIBERATELY UNUSED: `mark`
   (the large site mark — there is no page big enough for it), `return` (the
   note's own numeral already goes back), `link` (no per-heading anchor is
   offered), `copy`, `top` and `feed` (no visible feed link exists; the index
   advertises the feed in the head only). They stay drawn and available.
   -------------------------------------------------------------------------- */

/* The scale for the essay marks: pixels per board unit. `board.css` declares
   it too; this is the site's value, and it wins because this file is
   concatenated after the generated one. 1.3px makes the widest mark — 40
   units — 52px. A mark is NEVER scaled on its own: set the scale once and let
   the marks differ in size, the way diagrams do in a Go book. */
:root {
  --board-u: 1.3px;
}

/* The shared plumbing. A box, filled with the text's colour, with the drawing
   cut out of it. `mask` is still prefixed for WebKit, and both properties are
   declared in full rather than via the shorthand so that a browser with only
   the prefixed form gets every part of it. */
.masthead .wordmark::before,
.theme-toggle::before,
p.close::after,
.notes a[href^="http"]::after {
  content: "";
  display: inline-block;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

/* THE SITE MARK, beside the wordmark on every page: black, white, and the
   empty point between them, cut bolder for small sizes. */
.masthead .wordmark::before {
  -webkit-mask-image: var(--g-mark-small);
          mask-image: var(--g-mark-small);
  width: 1.35em;
  height: 1.35em;
  margin-right: .45em;
  vertical-align: -.34em;
}

/* THE COLOUR MODE CONTROL'S POINT. One intersection, in each of the three
   states an intersection can be in, selected by the same attribute the palette
   is selected by: no attribute means the device decides and the point is
   empty, light is the white stone, dark is the black one. The board's own
   grammar does the work — a stone cuts the lines that cross it, so the empty
   state is the only one that shows the board underneath.

   THIS IS THE ONE GLYPH ON THE SITE THAT IS A CONTROL RATHER THAN AN ECHO, so
   the button it sits in carries words of its own. See `render/essay.mjs`. */
.theme-toggle::before {
  -webkit-mask-image: var(--g-mode-system);
          mask-image: var(--g-mode-system);
  width: 1.3em;
  height: 1.3em;
  vertical-align: -.3em;
}

:root[data-theme="light"] .theme-toggle::before {
  -webkit-mask-image: var(--g-mode-light);
          mask-image: var(--g-mode-light);
}

:root[data-theme="dark"] .theme-toggle::before {
  -webkit-mask-image: var(--g-mode-dark);
          mask-image: var(--g-mode-dark);
}

/* THE END MARK: one stone, played after the last line of the essay. */
p.close::after {
  -webkit-mask-image: var(--g-end);
          mask-image: var(--g-end);
  width: .62em;
  height: .62em;
  margin-left: .4em;
  vertical-align: .01em;
}

/* A STONE PLAYED OFF THE CORNER OF THE BOARD, after a source that leaves the
   site. The attribute selector is the whole rule: a note's own numeral links
   to `#r7` and a cross-reference to another essay links to `/essays/…`, so
   neither takes the mark, and no renderer has to know which is which. */
.notes a[href^="http"]::after {
  -webkit-mask-image: var(--g-out);
          mask-image: var(--g-out);
  width: .86em;
  height: .86em;
  margin: 0 .1em 0 .22em;
  vertical-align: -.08em;
}

/* A PULL QUOTE CARRIES NO GLYPH. It used to open on two stones at cap height,
   hung in the margin on a wide screen. The quote is already set apart by its
   size, its colour and the space around it, and the stones were a third way of
   saying the same thing. Its indent went with them: the quote now sits on the
   measure like everything else. */

/* THE SECTION BREAK: black, white, black. A block, centred, sized in pixels
   rather than ems because it belongs to the page and not to a line of type. */
p.brk {
  display: block;
  width: 60px;
  height: 12px;
  margin: 2.75rem auto 2.5rem;
  background: currentColor;
  -webkit-mask: var(--g-break) center / contain no-repeat;
          mask: var(--g-break) center / contain no-repeat;
}

/* PREVIOUS / ALL ESSAYS / NEXT carry no arrows. Three words in a row at the
   foot of a page, each one naming where it goes, do not need a drawing each to
   say it a second time. */

/* THE ESSAY MARK. `board.css` gives `.m` its mask and its box — the box comes
   from the mark's own size in board units and the scale above — so all that is
   left here is where it sits and how loud it is. --ink-2, because it is a
   signature and not a heading.

   Under the byline on an essay page that has no header still. Where a still
   landed, the still is the page's one ornament and the mark stands down. */
header .m {
  display: block;
  margin: 1.5rem 0 0;
  color: var(--ink-2);
}


/* --------------------------------------------------------------------------
   16. PRINT
   LIFTED, extended to drop the three invented navigational elements — they
   are all screen affordances and carry no meaning on paper.
   -------------------------------------------------------------------------- */

@media print {
  body {
    background: #fff;
    color: #000;
    font-size: 11pt;
  }

  .page {
    max-width: none;
    padding: 0;
  }

  sup.ref a,
  .notes a,
  .notes .back,
  article a {
    color: #000;
  }

  .notes li:target {
    background: none;
    box-shadow: none;
  }

  .masthead,
  nav.essay-nav,
  .skip-link {
    display: none;
  }

  figure.wide {
    margin-inline: 0;
  }

  /* THE GLYPHS DO NOT PRINT. Every one of them is a mask over a background,
     and a background is the first thing a print engine drops — the box would
     survive and the drawing would not. So they are removed outright, and the
     one glyph that carried meaning rather than ornament gets its character
     back: the section break returns to an em dash on paper. */
  p.close::after,
  .notes a[href^="http"]::after,
  header .m {
    display: none;
  }

  /* The index's images are a screen affordance: on paper the titles carry the
     page on their own and eleven halftones would carry nothing. */
  .essay-list .cover,
  .essay-list .thumb {
    display: none;
  }

  p.brk {
    display: block;
    width: auto;
    height: auto;
    margin: 2.6rem 0;
    background: none;
    -webkit-mask: none;
            mask: none;
  }

  p.brk::before {
    content: "—";
  }
}
