/* SakinLab core — tokens, themes, shell.
   Rules: general design guidelines (kept outside the repo) and docs/site-stil-rehberi.md
   Type: Geist everywhere; EB Garamond only for the name and tagline on the home page.
   Structure comes from hairlines and alignment, not from boxes. */

/* Geist (variable) and, for Arabic code points only, IBM Plex Sans Arabic under the same name, so every Geist
   stack shows Arabic in Plex and Latin in Geist. Both are declared in the same weight bands: the browser only
   combines faces whose descriptors match. Canvas/export fonts are separate stacks and are not affected. */
@font-face { font-family: "Geist"; src: url("/assets/fonts/geist.woff2") format("woff2"); font-weight: 100 449; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist"; src: url("/assets/fonts/geist.woff2") format("woff2"); font-weight: 450 549; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist"; src: url("/assets/fonts/geist.woff2") format("woff2"); font-weight: 550 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist"; src: url("/assets/fonts/ibm-plex-sans-arabic-400.woff2") format("woff2"); font-weight: 100 449; font-style: normal; font-display: swap; unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+FB50-FDFF, U+FE70-FEFF; }
@font-face { font-family: "Geist"; src: url("/assets/fonts/ibm-plex-sans-arabic-500.woff2") format("woff2"); font-weight: 450 549; font-style: normal; font-display: swap; unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+FB50-FDFF, U+FE70-FEFF; }
@font-face { font-family: "Geist"; src: url("/assets/fonts/ibm-plex-sans-arabic-600.woff2") format("woff2"); font-weight: 550 900; font-style: normal; font-display: swap; unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+FB50-FDFF, U+FE70-FEFF; }
@font-face { font-family: "EB Garamond"; src: url("/assets/fonts/eb-garamond.woff2") format("woff2"); font-weight: 400 800; font-style: normal; font-display: swap; }

/* ---- Palette: the one place for the accent colours (site and tools) ----
   Each appearance gives only its sources: --c-accent (action, selection, focus) and --c-alert (errors, deleting,
   anything that must stop the eye), with the text colour that sits on each fill, plus two status sources.
   Every other tone is worked out from those, the same way in all four appearances (oklab mix with the page's
   paper and ink): hover and press are the fill pulled towards the ink, soft is a light wash for selected rows,
   line is a thin outline, text is the tone for words on the page (it keeps 4.5:1 on the paper).
   Change a source here and everything that uses it follows. Preview: /system/palette/ */
/* Two families only: Day and Night share one set, Meadow and Sky share another. The page colour does the rest. */
:root, [data-appearance="light"], [data-appearance="dark"] {
  --c-accent: #2E6BFF; --c-on-accent: #FFFFFF;   /* 3:1 or more on both papers, white text 4.5:1 on it */
  --c-alert:  #E03E2F; --c-on-alert:  #FFFFFF;
  --c-ok: #22A060; --c-caution: #D99A1E;
  --c-text-mix: 86%;   /* how much of the source stays in text tones (the rest is ink); lower where contrast needs it */
}
[data-appearance="green"], [data-appearance="blue"] {   /* red turns muddy on the coloured papers; the alert is a clear orange */
  --c-accent: #FFD166; --c-on-accent: #1D2951;
  --c-alert:  #FF9055; --c-on-alert:  #2A1408;
  --c-ok: #9BE3B8; --c-caution: #FFD166;
  --c-text-mix: 52%;   /* the orange needs more ink to read as text on green */
}
:root, [data-appearance] {   /* also on an element that sets its own appearance (the palette preview) */
  --c-accent-hover: color-mix(in oklab, var(--c-accent) 86%, var(--ink));
  --c-accent-press: color-mix(in oklab, var(--c-accent) 74%, var(--ink));
  --c-accent-soft:  color-mix(in oklab, var(--c-accent) 14%, var(--paper));
  --c-accent-line:  color-mix(in oklab, var(--c-accent) 45%, var(--paper));
  --c-accent-text:  color-mix(in oklab, var(--c-accent) var(--c-text-mix), var(--ink));
  --c-alert-hover:  color-mix(in oklab, var(--c-alert) 86%, var(--ink));
  --c-alert-press:  color-mix(in oklab, var(--c-alert) 74%, var(--ink));
  --c-alert-soft:   color-mix(in oklab, var(--c-alert) 14%, var(--paper));
  --c-alert-line:   color-mix(in oklab, var(--c-alert) 45%, var(--paper));
  --c-alert-text:   color-mix(in oklab, var(--c-alert) var(--c-text-mix), var(--ink));
  --c-ok-soft:      color-mix(in oklab, var(--c-ok) 14%, var(--paper));
  --c-caution-soft: color-mix(in oklab, var(--c-caution) 16%, var(--paper));
}

:root {
  color-scheme: light;
  --paper: #FAFAFA;
  --ink: #171717;
  --ink-soft: #5C5C5C;
  --ink-muted: #8C8C8C;
  --hair: #ECECEC;
  --hair-strong: #D4D4D4;
  --wash: rgb(23 23 23 / .035);
  --chip: rgb(237 237 237 / .6);
  --chip-hover: rgb(226 226 226 / .9);
  --rule-bg: #F5F5F5;
  --rule-edge: #E4E4E4;
  --rule-tick: #BDBDBD;
  --rule-label: #8F8F8F;
  --rule-band: rgb(23 23 23 / .07);
  --mat-ink: #000;
  --mat-alpha: .07;
  --mat-reveal: .18;
  --focus: var(--c-accent);
  --guide: rgb(242 72 34 / .55);

  --font-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-serif: "EB Garamond", Georgia, "Times New Roman", serif;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 20px; --s6: 24px; --s8: 32px; --s10: 40px; --s12: 48px; --s16: 64px;
  --ruler: 20px;
  --cols: 16;
  --m: calc((100vw - var(--ruler)) / var(--cols));
  --u: calc(var(--m) / 5);
  --r-chip: 6px;

  --duration-press: 120ms;
  --duration-fast: 160ms;
  --duration-ui: 220ms;
  --duration-panel: 280ms;
  --duration-scene: 400ms;
  --ease-out-ui: cubic-bezier(.23, 1, .32, 1);
  --ease-move: cubic-bezier(.77, 0, .175, 1);
  --ease-spring: linear(0, 0.142 7%, 0.380 14%, 0.588 21%, 0.740 29%, 0.841 36%,
    0.905 43%, 0.944 50%, 0.968 57%, 0.982 64%, 0.990 71%, 0.994 79%,
    0.997 86%, 0.998 93%, 1);
  --duration-spring: 510ms;
  /* Arrivals (master-guidelines §12; the bencho.dev / emilkowal.ski vocabulary): one decisive ease-out for what
     appears, a small overshoot only for what pops in once (a badge, a mark), never for panels or navigation */
  --ease-out-strong: cubic-bezier(.22, 1, .36, 1);
  --ease-pop: cubic-bezier(.24, 1.34, .38, 1);
  --duration-reveal: 320ms;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --paper: #161616; --ink: #EDEDED; --ink-soft: #A6A6A6; --ink-muted: #767676;
    --hair: #2A2A2A; --hair-strong: #404040; --wash: rgb(237 237 237 / .04);
    --chip: rgb(44 44 44 / .75); --chip-hover: rgb(58 58 58 / .9);
    --rule-bg: #1A1A1A; --rule-edge: #262626; --rule-tick: #474747; --rule-label: #7D7D7D; --rule-band: rgb(237 237 237 / .08);
    --mat-ink: #fff; --mat-alpha: .06; --mat-reveal: .14; --guide: rgb(255 120 90 / .6);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark; --guide: rgb(255 120 90 / .6);
  --paper: #161616; --ink: #EDEDED; --ink-soft: #A6A6A6; --ink-muted: #767676;
  --hair: #2A2A2A; --hair-strong: #404040; --wash: rgb(237 237 237 / .04);
  --chip: rgb(44 44 44 / .75); --chip-hover: rgb(58 58 58 / .9);
  --rule-bg: #1A1A1A; --rule-edge: #262626; --rule-tick: #474747; --rule-label: #7D7D7D; --rule-band: rgb(237 237 237 / .08);
  --mat-ink: #fff; --mat-alpha: .06; --mat-reveal: .14;
}
:root[data-theme="green"] {
  /* Meadow: the green of a cutting mat, pale yellow guide lines */
  color-scheme: dark;
  --paper: #305F56; --ink: #F1F4EA; --ink-soft: rgb(241 244 234 / .74); --ink-muted: rgb(241 244 234 / .52);
  --hair: rgb(241 244 234 / .12); --hair-strong: rgb(241 244 234 / .24); --wash: rgb(241 244 234 / .06);
  --chip: rgb(241 244 234 / .09); --chip-hover: rgb(241 244 234 / .16);
  --rule-bg: #2A544C; --rule-edge: #3C6D63; --rule-tick: #7E9F95; --rule-label: #B7CFC8; --rule-band: rgb(241 244 234 / .1);
  --mat-ink: #F3EBA0; --mat-alpha: .12; --mat-reveal: .26;
  --guide: rgb(243 211 91 / .8);
}
:root[data-theme="blue"] {
  /* Sky: cyanotype blue, white guide lines */
  color-scheme: dark;
  --paper: #2B407E; --ink: #F4F6FD; --ink-soft: rgb(242 246 255 / .76); --ink-muted: rgb(242 246 255 / .54);
  --hair: rgb(242 246 255 / .13); --hair-strong: rgb(242 246 255 / .26); --wash: rgb(242 246 255 / .07);
  --chip: rgb(242 246 255 / .1); --chip-hover: rgb(242 246 255 / .18);
  --rule-bg: #273B74; --rule-edge: #3A508F; --rule-tick: #7B8ABC; --rule-label: #B7C1E2; --rule-band: rgb(242 246 255 / .1);
  --mat-ink: #FFFFFF; --mat-alpha: .11; --mat-reveal: .24;
  --guide: rgb(255 209 102 / .85);
}
@media (prefers-reduced-motion: reduce) {
  :root { --duration-panel: 1ms; --duration-scene: 1ms; --duration-spring: 1ms; --duration-reveal: 1ms; }
}/* Page transitions. The workspace (rulers, settings, nav) stays still; only the sheet changes.
   The old sheet lifts away quickly; the new one settles in on a spring.
   The ruler band travels from what you clicked to where you land (core.js). */


@view-transition { navigation: auto; }
.ruler-x { view-transition-name: ruler-x; }
.ruler-y { view-transition-name: ruler-y; }
.ruler-corner { view-transition-name: ruler-corner; }
.settings { view-transition-name: settings; }
:where(body:not([data-app])) .site-nav { view-transition-name: site-nav; }
::view-transition-group(ruler-x), ::view-transition-group(ruler-y), ::view-transition-group(ruler-corner),
::view-transition-group(settings), ::view-transition-group(site-nav) { animation: none; }
::view-transition-old(ruler-x), ::view-transition-old(ruler-y), ::view-transition-old(ruler-corner),
::view-transition-old(settings) { animation: none; opacity: 0; }
::view-transition-new(ruler-x), ::view-transition-new(ruler-y), ::view-transition-new(ruler-corner),
::view-transition-new(settings) { animation: none; }
::view-transition-old(root) { animation: sheet-out 150ms var(--ease-out-ui) both; }
::view-transition-new(root) { animation: sheet-in var(--duration-spring) var(--ease-spring) 40ms both; }
@keyframes sheet-out { to { opacity: 0; transform: translateY(-6px); filter: blur(2px); } }
@keyframes sheet-in { from { opacity: 0; transform: translateY(14px); } }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}/* Base */


*, *::before, *::after { box-sizing: border-box; }
html { height: 100%; background: var(--paper) var(--grain-page, none) 0 0 / var(--grain-size, 128px); -webkit-text-size-adjust: 100%; }
body {
  margin: 0; min-height: 100%;
  background: var(--paper) var(--grain-page, none) 0 0 / var(--grain-size, 128px); color: var(--ink);
  font: 400 14px/1.5 var(--font-sans);
  font-feature-settings: "ss01";
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
}
a { color: inherit; text-decoration: none; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 3px; }
.i18n-pending [data-i18n] { visibility: hidden; }
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
:where(body:not([data-app])) .skip-link {
  position: fixed; left: calc(var(--ruler) + var(--s3)); top: calc(var(--ruler) + var(--s3)); z-index: 40;
  padding: 6px 10px; background: var(--paper); color: var(--ink); border-radius: var(--r-chip);
  box-shadow: 0 0 0 1px var(--hair-strong); transform: translateY(-300%);
}
:where(body:not([data-app])) .skip-link:focus { transform: none; }/* Rulers (all pages) and cut mat (home only: data-surface="mat"). Both are SVG, so they stay sharp at any zoom. */


.ruler { position: fixed; z-index: 30; pointer-events: none; background: var(--rule-bg) var(--grain-rule, none) 0 0 / var(--grain-size, 128px); overflow: hidden; }
.ruler svg { display: block; overflow: visible; }
.ruler-x { top: 0; left: 0; right: 0; height: var(--ruler); padding-left: var(--ruler); border-bottom: 1px solid var(--rule-edge); }
.ruler-y { top: var(--ruler); left: 0; bottom: 0; width: var(--ruler); border-right: 1px solid var(--rule-edge); }
.ruler-corner {
  position: fixed; z-index: 31; top: 0; left: 0; width: var(--ruler); height: var(--ruler);
  appearance: none; margin: 0; padding: 0; border: 0; border-right: 1px solid var(--rule-edge); border-bottom: 1px solid var(--rule-edge);
  background: var(--rule-bg) var(--grain-rule, none) 0 0 / var(--grain-size, 128px); color: var(--rule-label); cursor: pointer;
  font: 600 8px/1 var(--font-sans); letter-spacing: .02em; display: grid; place-items: center;
  transition: color var(--duration-fast) var(--ease-out-ui);
}
.ruler-corner:hover { color: var(--ink); }
@media (hover: none), (max-width: 599px) { .ruler-corner { font-size: 0; } }
.ruler path { fill: none; stroke-width: 1; shape-rendering: crispEdges; vector-effect: non-scaling-stroke; }
.ruler .r-minor { stroke: var(--rule-tick); opacity: .7; }
.ruler .r-major { stroke: var(--rule-tick); }
.ruler .r-cursor { stroke: var(--ink); opacity: .55; }
.ruler .r-band { fill: var(--rule-band); }
.ruler text { font: 500 9px/1 var(--font-sans); fill: var(--rule-label); font-variant-numeric: tabular-nums; transition: opacity var(--duration-fast) var(--ease-out-ui); }
.ruler .r-labels { transition: opacity var(--duration-fast) var(--ease-out-ui); }
.ruler .r-band-label { fill: var(--ink); }
.mat { position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: 0; transition: opacity 700ms var(--ease-out-ui); }
.mat.is-doc { position: absolute; inset: auto; top: 0; left: 0; }
.mat[data-mode="grid"] { --k: .5; }
.mat-reveal[data-mode="grid"] { --k: .75; }
.mat .mat-label { font: 500 9px/1 var(--font-sans); fill: var(--mat-ink); opacity: calc(var(--mat-alpha) * 4); letter-spacing: .02em; }
.mat.is-ready { opacity: 1; }
.mat path { fill: none; stroke: var(--mat-ink); stroke-width: 1; vector-effect: non-scaling-stroke; }
.mat .mat-minor { opacity: calc(var(--mat-alpha) * var(--k, 1) * .55); shape-rendering: crispEdges; }
.mat .mat-major { opacity: calc(var(--mat-alpha) * var(--k, 1)); shape-rendering: crispEdges; }
.mat .mat-angle { opacity: calc(var(--mat-alpha) * 1.1); }
.mat .mat-arc { opacity: calc(var(--mat-alpha) * .9); }
.mat-reveal, .mat-reveal[data-mode] { --mat-alpha: var(--mat-reveal);
  --mx: -999px; --my: -999px;
  -webkit-mask-image: radial-gradient(circle 200px at var(--mx) var(--my), #000 0%, rgb(0 0 0 / .45) 45%, transparent 100%);
          mask-image: radial-gradient(circle 200px at var(--mx) var(--my), #000 0%, rgb(0 0 0 / .45) 45%, transparent 100%);
}
body[data-surface="none"] :is(.ruler, .ruler-corner) { display: none; }
body[data-surface="none"] { --ruler: 0px; }
@media (hover: none), (max-width: 599px) { :root { --ruler: 14px; } }/* Module grid: the site's unit. Anything placed in .cells snaps to modules and reflows by column count. */


:where(body:not([data-app])) .cells { display: grid; grid-template-columns: repeat(var(--cols), var(--m)); grid-auto-rows: var(--m); }
:where(body:not([data-app])) .cell { min-width: 0; min-height: 0; }/* Readouts — the small details of a design tool */


.readout {
  position: fixed; z-index: 29;
  font: 500 11px/1 var(--font-sans); letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
  pointer-events: none;
}
.readout-frame { top: calc(var(--ruler) + var(--s3)); left: calc(var(--ruler) + var(--s3)); display: flex; gap: var(--s2); }
.readout-frame b { font-weight: 500; color: var(--ink-soft); }
.readout-xy {
  bottom: var(--s3); left: calc(var(--ruler) + var(--s2)); display: flex; gap: var(--s3);
  opacity: 0; transition: opacity var(--duration-ui) var(--ease-out-ui);
}
.readout-xy.is-on { opacity: 1; }
.readout-xy span { min-width: 5.5ch; }
.readout-xy.in-slot, .readout-frame.in-slot { position: static; z-index: auto; }
.readout-frame, .readout-xy { line-height: 1; }
@media (hover: none), (max-width: 599px) { .readout-xy { display: none; } }/* Settings: appearance (site); language + appearance (apps) */


.settings {
  position: fixed; z-index: 32;
  top: calc(var(--ruler) + var(--s3)); inset-inline-end: var(--s3);
  display: flex; align-items: center; gap: 2px;
  font: 500 12px/1 var(--font-sans);
}
.settings.in-slot { position: static; }
.settings button {
  appearance: none; border: 0; margin: 0; min-width: 28px; height: 28px; padding: 0 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--ink-muted); font: inherit; letter-spacing: .01em;
  border-radius: var(--r-chip); cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out-ui), background-color var(--duration-fast) var(--ease-out-ui), transform var(--duration-press) var(--ease-out-ui);
}
.settings button[aria-pressed="true"] { color: var(--ink); }
.settings button:active { transform: scale(.94); }
@media (hover: hover) and (pointer: fine) { .settings button:hover { color: var(--ink); background: var(--wash); } }
.settings .lang { position: relative; display: flex; }
.settings .lang-btn { gap: 3px; padding: 0 6px 0 8px; color: var(--ink-soft); }
.settings .lang-btn svg { width: 10px; height: 10px; transition: transform var(--duration-ui) var(--ease-out-ui); }
.settings .lang-btn[aria-expanded="true"] { color: var(--ink); background: var(--wash); }
.settings .lang-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.settings .lang-list {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 50; min-width: 148px;
  display: grid; gap: 1px; padding: 4px; border-radius: 8px;
  background: var(--paper); box-shadow: 0 0 0 1px var(--hair-strong), 0 8px 24px rgb(0 0 0 / .1);
  transform-origin: top right;
}
.settings .lang-list[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .settings .lang-list:not([hidden]) { animation: menu-in var(--duration-ui) var(--ease-out-ui); }
  @keyframes menu-in { from { opacity: 0; transform: translateY(-4px) scale(.98); } }
}
.settings .lang-list button {
  width: 100%; height: 30px; padding: 0 10px; justify-content: space-between; gap: 12px;
  color: var(--ink-soft); font: 500 12.5px/1 var(--font-sans);
}
.settings .lang-list button small { font-size: 10.5px; color: var(--ink-muted); letter-spacing: .03em; }
.settings .lang-list button[aria-checked="true"] { color: var(--ink); background: var(--wash); }
.settings .sep { width: 1px; height: 14px; background: var(--hair-strong); margin: 0 4px; }
.settings .themes { display: flex; gap: 1px; }
.settings .themes button { width: 28px; padding: 0; }
.settings .themes svg { width: 15px; height: 15px; display: block; overflow: visible; }
.settings .themes button[aria-pressed="true"] { color: var(--ink); background: var(--wash); }
.settings .ic, .settings .ic path { transform-box: fill-box; transform-origin: center; transition: transform var(--duration-spring) var(--ease-spring); }
.settings [data-theme-set="green"] .ic path { transform-origin: bottom center; }
@media (hover: hover) and (pointer: fine) {
  .settings [data-theme-set="light"]:hover .ic { transform: rotate(45deg); }
  .settings [data-theme-set="dark"]:hover .ic { transform: rotate(-16deg); }
  .settings [data-theme-set="green"]:hover .b1 { transform: rotate(-7deg); }
  .settings [data-theme-set="green"]:hover .b2 { transform: rotate(5deg); }
  .settings [data-theme-set="green"]:hover .b3 { transform: rotate(9deg); }
  .settings [data-theme-set="blue"]:hover .ic { transform: translateX(1.5px); }
}/* Home navigation chips (from the reference frame) */


:where(body:not([data-app])) .site-nav { position: fixed; z-index: 20; left: 50%; bottom: clamp(28px, 9.2vh, 99px); transform: translateX(-50%); display: flex; gap: 4px; }
:where(body:not([data-app])) .chip {
  position: relative; display: inline-flex; align-items: center; height: 28px; padding: 0 10px;
  border-radius: var(--r-chip); background: var(--chip); color: var(--ink);
  font: 500 13px/1 var(--font-sans); letter-spacing: -.005em;
  transition: background-color var(--duration-fast) var(--ease-out-ui), transform var(--duration-press) var(--ease-out-ui);
}
:where(body:not([data-app])) .chip:active { transform: scale(.95); }
@media (hover: hover) and (pointer: fine) { :where(body:not([data-app])) .chip:not(.is-soon):hover { background: var(--chip-hover); } }
:where(body:not([data-app])) .chip.is-soon { color: var(--ink-muted); cursor: default; }
:where(body:not([data-app])) .chip.is-soon::after, :where(body:not([data-app])) .nav-link.is-soon::after {
  content: attr(data-soon); position: absolute; left: 50%; bottom: calc(100% + 6px); transform: translate(-50%, 3px);
  font: 500 11px/1 var(--font-sans); color: var(--ink-muted); white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity var(--duration-fast) var(--ease-out-ui), transform var(--duration-fast) var(--ease-out-ui);
}
:where(body:not([data-app])) .chip.is-soon:hover::after, :where(body:not([data-app])) .nav-link.is-soon:hover::after { opacity: 1; transform: translate(-50%, 0); }/* Inner pages: a sheet on the module grid. Columns are modules (--m); spacing is in minor units (--u).
   Desktop: 16 columns, content in 2–15. Tablet: 8, full width. Mobile: 4, full width. */


:where(body:not([data-app])) .sheet { position: relative; z-index: 1; padding: var(--ruler) 0 0 var(--ruler); min-height: 100svh; min-height: calc(var(--ruler) + round(down, 100svh - var(--ruler), var(--u))); box-sizing: border-box; display: flex; flex-direction: column; }
:where(body:not([data-app])) .mod { display: grid; grid-template-columns: repeat(var(--cols), var(--m)); }
:where(body:not([data-app])) .span-content { grid-column: 2 / -2; }
/* ---- Type on the grid: four rules, all from the square (--u) ----
   1. Small text: one line = one square (--line), the text in the middle of it.
   2. Large text (the lead): its cap height is one square, lines are two squares apart, and it is trimmed to its
      cap line and baseline (text-box), so its box starts on a grid line and its baselines fall on grid lines.
   3. Text starts --inset from the grid line it belongs to: the air a small line has above and below it in its
      square (square − cap height of 13.5px text, halved), so the gap is the same on every side.
   4. Icons and arrows sit in the middle of a square; gaps are whole squares.
   Font sizes stay in px (they don't change with the screen); everything that places them is in squares. */
:root { --line: var(--u); --cap-s: 9.5px; --inset: calc((var(--u) - var(--cap-s)) / 2); }
:where(body:not([data-app])) .masthead .inner { padding-inline: var(--inset); }
@media (max-width: 1219px) { :where(body:not([data-app])) .span-content { grid-column: 1 / -1; padding-inline: var(--u); }
  :where(body:not([data-app])) :is(.masthead, .colophon) .inner { padding-inline: calc(var(--u) + var(--inset)); } }

:where(body:not([data-app])) .masthead { box-sizing: border-box; padding-top: 1px; height: calc(var(--u) * 3); align-items: center; border-bottom: 1px solid var(--hair); background: color-mix(in srgb, var(--paper) 70%, transparent); }
:where(body:not([data-app])) .masthead .inner { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
:where(body:not([data-app])) .masthead .brand { display: flex; align-items: baseline; gap: var(--s3); min-width: 0; }
:where(body:not([data-app])) .masthead .brand > :first-child { font: 600 14px/1 var(--font-sans); letter-spacing: -.01em; }
:where(body:not([data-app])) .masthead .brand span { font-size: 13px; color: var(--ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
:where(body:not([data-app])) .masthead .tail { display: flex; align-items: center; gap: var(--s1); }
:where(body:not([data-app])) .nav-link {
  position: relative; display: inline-flex; align-items: center; height: 28px; padding: 0 8px;
  font: 500 13px/1 var(--font-sans); color: var(--ink-soft); border-radius: var(--r-chip);
  transition: color var(--duration-fast) var(--ease-out-ui);
}
:where(body:not([data-app])) .nav-link::before {
  content: ""; position: absolute; left: 8px; right: 8px; bottom: 5px; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: left; transition: transform var(--duration-ui) var(--ease-out-ui);
}
@media (hover: hover) and (pointer: fine) { :where(body:not([data-app])) .nav-link:not(.is-soon):hover { color: var(--ink); } :where(body:not([data-app])) .nav-link:not(.is-soon):hover::before { transform: scaleX(1); } }
:where(body:not([data-app])) .nav-link[aria-current="page"] { color: var(--ink); }
:where(body:not([data-app])) .nav-link[aria-current="page"]::before { transform: scaleX(1); }
:where(body:not([data-app])) .nav-link.is-soon { color: var(--ink-muted); cursor: default; }
:where(body:not([data-app])) .nav-link.is-soon::after { bottom: auto; top: calc(100% + 2px); transform: translate(-50%, -3px); }
:where(body:not([data-app])) .nav-link.is-soon:hover::after { transform: translate(-50%, 0); }
:where(body:not([data-app])) .masthead .sep { width: 1px; height: 14px; background: var(--hair-strong); margin: 0 var(--s2); }/* Intro: label in the first 4 content modules, lead after it. Lead cap height = 1u. */


:where(body:not([data-app])) .intro { box-sizing: border-box; height: calc(var(--m) * 3 - var(--u) * 3); padding-top: calc(var(--m) * 1.2); align-items: start; align-content: start; }
/* Type on the grid (see the style guide): small text takes one minor square per line (line-height = u), centred in it;
   the large lead has two squares per line and its baseline on a grid line (the first on module line 2). */
:where(body:not([data-app])) .intro .label { grid-column: 2 / span 4; padding-left: var(--inset); display: flex; align-items: baseline; gap: var(--s2); margin: 0; font: 600 14px/var(--line) var(--font-sans); letter-spacing: -.01em; }
:where(body:not([data-app])) .intro .label small { font: 500 12px/1 var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
:where(body:not([data-app])) .intro .lead { grid-column: 6 / span 9; padding-left: var(--inset); margin: 0; max-width: 30ch; font: 400 calc(var(--u) / .71)/calc(var(--u) * 2) var(--font-sans); letter-spacing: -.022em; text-wrap: balance; text-box: trim-both cap alphabetic; margin: calc(-1 * var(--inset)) 0 var(--inset); }
@supports not (text-box: trim-both cap alphabetic) { :where(body:not([data-app])) .intro .lead { margin-top: calc(var(--u) * -.45 - var(--inset)); margin-bottom: calc(var(--u) * -.55 + var(--inset)); } }
:where(body:not([data-app])) .intro .meta { grid-column: 6 / span 9; padding-left: var(--inset); margin: var(--line) 0 0; font-size: 13px; line-height: var(--line); color: var(--ink-muted); }
:where(body:not([data-app])) .intro :is(.lead, .meta) a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--hair-strong); text-underline-offset: .18em; text-decoration-thickness: 1px; transition: text-decoration-color var(--duration-fast) var(--ease-out-ui); }
:where(body:not([data-app])) .intro :is(.lead, .meta) a:hover { text-decoration-color: currentColor; }/* Index rows: one module tall; hairlines fall on module lines */


:where(body:not([data-app])) .index { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--hair); }
:where(body:not([data-app])) .index > li { border-bottom: 1px solid var(--hair); }
:where(body:not([data-app])) .row { position: relative; min-height: 0; align-items: start; align-content: start; box-sizing: border-box; padding: calc(var(--u) - 1px) 0 var(--u); grid-template-columns: repeat(14, var(--m)); } /* one empty square above and below the line (was a whole module) */
/* one square per line, each centred in its square; the first line sits in the row's middle square */
/* the icon in the middle of the first square, the name right after it (--inset from the line); the arrow in the second-last square */
:where(body:not([data-app])) .row .glyph { width: var(--u); height: var(--u); margin-right: calc(var(--inset) - var(--s3)); }
:where(body:not([data-app])) .row .glyph svg { width: 16px; height: 16px; flex: none; } /* fits its square (like the apps' header logo) */
:where(body:not([data-app])) .row::before {
  content: ""; position: absolute; inset: 0; background: var(--wash); border-radius: 4px;
  opacity: 0; transition: opacity var(--duration-fast) var(--ease-out-ui);
}
:where(body:not([data-app])) .row > :not(.visually-hidden) { position: relative; }
:where(body:not([data-app])) .row .name { grid-column: 1 / span 4; height: var(--line); display: flex; align-items: center; gap: var(--s3); font: 500 15px/var(--line) var(--font-sans); letter-spacing: -.012em; }
/* a row without an icon (404): the name starts --inset from the line, like the label, the brand and the credit above and below it */
:where(body:not([data-app])) .row .name:not(:has(> .glyph)) { padding-left: var(--inset); }
:where(body:not([data-app])) .row .desc { grid-column: 5 / span 6; padding-left: var(--inset); padding-right: var(--u); color: var(--ink-soft); line-height: var(--line); text-wrap: pretty; }
:where(body:not([data-app])) .row .kind { grid-column: 11 / span 3; padding-left: var(--inset); color: var(--ink-muted); font-size: 13px; line-height: var(--line); }
:where(body:not([data-app])) .row .go { grid-column: 14 / span 1; justify-self: end; align-self: start; margin: calc((var(--u) - 18px) / 2); margin-right: calc(var(--u) + (var(--u) - 18px) / 2); width: 18px; height: 18px; color: var(--ink-muted);
  transition: transform var(--duration-ui) var(--ease-out-ui), color var(--duration-fast) var(--ease-out-ui); }
:where(body:not([data-app])) .row:active { transform: scale(.995); }
/* Rows open in the same tab; the arrow on the right is its own link that opens a new tab */
:where(body:not([data-app])) .index > li { position: relative; }
:where(body:not([data-app])) .newtab { position: absolute; top: 0; right: 0; bottom: 0; width: calc(var(--m) * .75); z-index: 1; border-radius: 4px; }
:where(body:not([data-app])) .newtab:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
@media (hover: hover) and (pointer: fine) {
  :where(body:not([data-app])) .index > li:has(.newtab:hover) .row::before { opacity: 1; }
  :where(body:not([data-app])) .index > li:has(.newtab:hover) .go { color: var(--ink); transform: translate(3px, -3px) scale(1.12); }
}
@media (hover: hover) and (pointer: fine) {
  :where(body:not([data-app])) .row:hover::before { opacity: 1; }
  :where(body:not([data-app])) .row:hover .go { color: var(--ink); transform: translate(2px, -2px); }
}

:where(body:not([data-app])) .glyph { display: inline-grid; place-items: center; width: 20px; height: 20px; color: var(--ink); flex: none; }
:where(body:not([data-app])) .glyph svg { width: 100%; height: 100%; overflow: visible; }
:where(body:not([data-app])) .glyph .i-a, :where(body:not([data-app])) .glyph .i-b { transition: transform var(--duration-spring) var(--ease-spring); }
/* Icon motion (required on every app icon): i-a / i-b move by --dx / --dy on hover. Off with reduced motion. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  :where(body:not([data-app])) a:hover .glyph .i-a, :where(body:not([data-app])) a:hover .glyph .i-b, :where(body:not([data-app])) a:focus-visible .glyph .i-a, :where(body:not([data-app])) a:focus-visible .glyph .i-b { transform: translate(var(--dx, 0), var(--dy, 0)); }
}

:where(body:not([data-app])) .colophon { margin-top: auto; height: calc(var(--u) * 3); align-items: center; font-size: 12px; color: var(--ink-muted); }
:where(body:not([data-app])) .colophon .inner { display: flex; justify-content: space-between; padding-inline: var(--inset); }
@media (max-width: 1219px) { :where(body:not([data-app])) .colophon .inner { padding-inline: calc(var(--u) + var(--inset)); } }/* Tablet: 8 modules, full width */


@media (max-width: 1219px) {
  :where(body:not([data-app])) .intro .label { grid-column: 1 / span 2; padding-left: calc(var(--u) + var(--inset)); }
  :where(body:not([data-app])) .intro .lead, :where(body:not([data-app])) .intro .meta { grid-column: 3 / span 6; padding-right: var(--u); }
  :where(body:not([data-app])) .index { margin-inline: var(--u); }
  :where(body:not([data-app])) .row { grid-template-columns: repeat(8, calc(var(--m) - var(--u) / 4)); }
  :where(body:not([data-app])) .row .name { grid-column: 1 / span 3; }
  :where(body:not([data-app])) .row .desc { grid-column: 4 / span 4; }
  :where(body:not([data-app])) .row .kind { display: none; }
  :where(body:not([data-app])) .row .go { grid-column: 8; }
}/* Mobile: 4 modules, stacked rows */

@media (max-width: 613px) {
  :where(body:not([data-app])) .masthead .brand span { display: none; }
  /* phones: the pages fit the screen without scrolling. One empty square under the masthead and under the intro */
  :where(body:not([data-app])) .intro { height: auto; padding-top: var(--u); padding-bottom: var(--u); }
  :where(body:not([data-app])) .colophon { height: calc(var(--u) * 2); } /* two squares tall on phones, the text in the lower one */
  :where(body:not([data-app])) .colophon .inner { align-self: end; }
  :where(body:not([data-app])) .intro .label, :where(body:not([data-app])) .intro .lead, :where(body:not([data-app])) .intro .meta { grid-column: 1 / -1; padding-inline: calc(var(--u) + var(--inset)); }
  /* phone: the label in the square below the masthead area, the lead's baseline on module line 2 */
  :where(body:not([data-app])) .intro .lead { margin-top: calc(var(--line) - var(--inset)); }
  :where(body:not([data-app])) .row { grid-template-columns: repeat(4, 1fr); row-gap: 0; padding: calc(var(--u) - 1px) 0 var(--u); min-height: 0; align-content: start; } /* one empty square above and below */
  :where(body:not([data-app])) .row .name { grid-column: 1 / span 3; }
  :where(body:not([data-app])) .row .go { grid-column: 4; grid-row: 1; }
  :where(body:not([data-app])) .newtab { bottom: auto; height: calc(var(--u) * 4); width: calc(var(--u) * 4); right: 0; }
  :where(body:not([data-app])) .row .desc { grid-column: 1 / -1; padding-left: calc(var(--u) + var(--inset)); text-wrap: balance; }
}

body:not([data-frame]) .readout-frame { display: none; }

@media (prefers-reduced-motion: no-preference) {
  :where(body:not([data-app])) .enter { animation: enter 600ms var(--ease-out-ui) both; animation-delay: var(--delay, 0ms); }
  @keyframes enter { from { opacity: 0; transform: translateY(4px); } }
}/* Grain: fine photographic noise in the background (text stays crisp), as if blended with overlay, so colours keep
   their value (#FAFAFA stays #FAFAFA on average). Strength per appearance: --grain. */


:root { --grain: 1; }
:root[data-appearance="dark"] { --grain: .14; }
:root[data-appearance="green"], :root[data-appearance="blue"] { --grain: .1; }
/* core.js bakes the grain into the background colour itself (--grain-page, --grain-rule; tools: --grain-canvas):
   a small tile at device-pixel size, the overlay blend worked out per pixel. No blend layer on screen, so nothing
   can drop the blend and show raw noise (iOS did, while scrolling and on tool canvases). */
:where(body:not([data-app])) .row .name .ver { font: 500 11px/1 var(--font-sans); color: var(--ink-muted); font-variant-numeric: tabular-nums; letter-spacing: .01em; }
.tm { font-size: .62em; vertical-align: .55em; margin-left: .05em; letter-spacing: 0; font-weight: 500; }/* Footer on the module grid: the X/Y readout starts on the same column as the descriptions above */

@media (min-width: 1220px) {
  :where(body:not([data-app])) .colophon .inner { display: grid; grid-template-columns: repeat(14, var(--m)); align-items: center; padding-inline: 0; }
  :where(body:not([data-app])) .colophon .inner > * { padding-inline: var(--inset); }
  :where(body:not([data-app])) .colophon .inner > :nth-child(1) { grid-column: 1 / 5; }
  :where(body:not([data-app])) .colophon .inner > [data-frame-slot] { grid-column: 5 / 11; }
  :where(body:not([data-app])) .colophon .inner > [data-readout-slot] { grid-column: 14 / 15; justify-self: start; } /* X/Y in the last module */
}
.readout-xy.in-slot { font-size: 11px; }
/* Phones: brand, three menu links and the four appearances share one line — tighten so nothing overlaps */
@media (max-width: 480px) {
  :where(body:not([data-app])) .masthead .inner { gap: var(--s2); }
  :where(body:not([data-app])) .masthead .tail { gap: 0; }
  :where(body:not([data-app])) .nav-link { padding: 0 6px; }
  :where(body:not([data-app])) .nav-link::before { left: 6px; right: 6px; }
  :where(body:not([data-app])) .masthead .sep { margin: 0 var(--s1); }
}
/* Narrower still: only the current appearance shows; tapping it steps to the next (core.js) */
@media (max-width: 440px) {
  :where(body:not([data-app])) .masthead .brand { flex-shrink: 0; }
  :where(body:not([data-app])) .masthead .settings .themes button:not([aria-pressed="true"]) { display: none; }
}
/* Pictures in text (About, later the blog): a plate on the module grid.
   The plate is --w modules wide (default 4) and sits on grid lines (core.js nudges its top onto a minor line).
   Top and bottom guides run to the page edges; the left guide runs the page (text beside it is --inset away, as
   everywhere); the right guide frames the box and fades out, so wider text above isn't cut.
   Rows inside sit on minor lines; the picture's bottom is on a grid line. Quiet colour, not the accent.
   Inside, centred: the picture, optional text, the caption.
     <figure class="plate sketch" style="--src: url(/assets/sketches/NAME.svg); --w: 4; --ratio: 1.5" data-measure>
       <span class="art" role="img" aria-label="What the drawing shows"></span><p>A line or two</p>
       <figcaption>Caption</figcaption></figure>
     <figure class="plate photo" style="--w: 4; --ratio: 1.5" data-measure>
       <img src="…" alt="…" width="…" height="…" loading="lazy"><figcaption>Caption</figcaption></figure>
   .photo fills its space (cropped); .is-mono: black and white. */
:where(body:not([data-app])) .sheet { overflow: clip; }          /* plate guides stop at the page edges */
:where(body:not([data-app])) .plate {
  position: relative; box-sizing: border-box; margin: calc(var(--u) * 1.5) 0 0;
  width: min(100%, calc(var(--m) * var(--w, 4)));
  /* one minor square of air around everything; every row is whole minor squares (text has a line height of u,
     the picture's height is rounded by core.js), so the plate and its picture sit on grid lines */
  display: grid; justify-items: center; align-content: start; row-gap: var(--u);
  padding: var(--u); text-align: center;
}
:where(body:not([data-app])) .plate::before, :where(body:not([data-app])) .plate::after { content: ""; position: absolute; pointer-events: none; }
/* top and bottom guides to the page edges; the right guide frames the box and fades out gently above and below */
:where(body:not([data-app])) .plate::before {
  --c: var(--hair-strong); inset: calc(var(--m) * -2) -300vw;
  background:
    linear-gradient(var(--c), var(--c)) left 0 top calc(var(--m) * 2) / 100% 1px no-repeat,
    linear-gradient(var(--c), var(--c)) left 0 bottom calc(var(--m) * 2) / 100% 1px no-repeat,
    linear-gradient(transparent calc(var(--m) * 1.6), var(--c) calc(var(--m) * 2), var(--c) calc(100% - var(--m) * 2), transparent calc(100% - var(--m) * 1.4))
      left calc(300vw + var(--pw, calc(var(--m) * var(--w, 4))) - 1px) top 0 / 1px 100% no-repeat;
}
/* the left guide runs the page, from under the masthead down towards the footer (core.js: --up, --down) */
:where(body:not([data-app])) .plate::after { top: calc(-1 * var(--up, 0px)); bottom: calc(-1 * var(--down, 0px)); left: 0; border-left: 1px solid var(--hair-strong);
  -webkit-mask: linear-gradient(to top, transparent, #000 var(--m)); mask: linear-gradient(to top, transparent, #000 var(--m)); }   /* fades out before the footer */
:where(body:not([data-app])) .plate > p, :where(body:not([data-app])) .plate figcaption { margin: 0; line-height: var(--u); }
:where(body:not([data-app])) .plate figcaption { font-size: 12px; color: var(--ink-muted); }
:where(body:not([data-app])) .sketch .art {
  display: block; width: 100%; aspect-ratio: var(--ratio, 1.5); background: var(--ink);
  -webkit-mask: var(--src) center bottom / contain no-repeat; mask: var(--src) center bottom / contain no-repeat;
}
/* the lines are plain vectors; a fine crayon grain goes over them: specks of paper colour at a fixed on-screen size,
   inside the drawing's own mask (one mask per element: combining masks drew a black band in Safari) */
:where(body:not([data-app])) .sketch .art { position: relative; }
:where(body:not([data-app])) .sketch .art::after {
  content: ""; position: absolute; inset: 0; background: var(--paper);
  -webkit-mask: url(/assets/sketches/grain.svg) 0 0 / 120px 120px repeat; mask: url(/assets/sketches/grain.svg) 0 0 / 120px 120px repeat;
}
:where(body:not([data-app])) .photo img { display: block; width: 100%; height: auto; aspect-ratio: var(--ratio, 1.5); object-fit: cover; }
:where(body:not([data-app])) .photo.is-mono img { filter: grayscale(1) contrast(1.05); }
/* Appearance buttons: side by side, each one square on the grid lines (small, like the rest of the toy). On the home page
   they sit in the first row of squares, one square in from the right edge; in the masthead, in its middle row of squares,
   the right edge on the content line. */
:where(body:not([data-app])) .settings .themes { gap: 0; }
:where(body:not([data-app])) .settings .themes button { width: var(--u); min-width: 0; height: var(--u); padding: 0; border-radius: 3px; }
:where(body:not([data-app])) .settings:not(.in-slot) { top: calc(var(--ruler) + var(--u)); inset-inline-end: var(--u); gap: 0; }
@media (min-width: 1220px) { :where(body:not([data-app])) .settings:not(.in-slot) { inset-inline-end: var(--m); } } /* on the content line, as in the masthead */
:where(body:not([data-app])) .masthead [data-settings-slot] { margin-inline-end: calc(-1 * var(--inset)); } /* the boxes end on the content line, not --inset before it */
/* Phones (home): with the buttons on the squares, the floating frame details keep only the page name and size */
@media (max-width: 480px) { .readout-frame > :nth-child(3) { display: none; } .readout-frame { white-space: nowrap; } }
/* Masthead: the separator stands on a module line (4 icon squares + one empty square before the content line), the last
   menu link ends one square before it. Footer: every item one square tall, text centred in it; on narrower screens the
   frame details start on the second module line. */
:where(body:not([data-app])) .masthead .sep { margin: 0 calc(var(--u) - 1px - var(--s1)) 0 calc(var(--u) - 8px - var(--s1)); }
@media (max-width: 480px) { :where(body:not([data-app])) .masthead .sep { margin: 0 calc(var(--u) - 1px) 0 calc(var(--u) - 6px); } }
:where(body:not([data-app])) .colophon .inner > * { line-height: var(--line); }
.readout-frame.in-slot, .readout-xy.in-slot { line-height: var(--line); }
.readout-xy.in-slot { gap: var(--s2); }
.readout-xy.in-slot span { min-width: 4.6ch; }
@media (max-width: 1219px) {
  :where(body:not([data-app])) .colophon .inner > [data-credit] { flex: 0 0 calc(var(--m) * 2 - var(--u) - var(--inset)); }
  :where(body:not([data-app])) .colophon .inner > [data-readout-slot] { flex: 0 0 calc(var(--u) * 4 - var(--inset)); padding-left: var(--inset); } /* X/Y in the last module */
  :where(body:not([data-app])) .colophon .inner > [data-frame-slot] { margin-right: auto; padding-left: var(--inset); }
}
/* A small action on the right of the page intro, on the label's line (Tools: TRT Edition; TRT: Sign out).
   Placed over the grid (it must not push the lead to a new row); its right edge on the content line. */
:where(body:not([data-app])) .intro:has(.intro-action) { position: relative; }
:where(body:not([data-app])) .intro .intro-action { position: absolute; top: calc(var(--m) * 1.2); right: var(--m); display: inline-flex; align-items: center; gap: var(--s1);
  height: var(--line); padding: 0 var(--inset); font: 500 13px/var(--line) var(--font-sans); color: var(--ink-soft); border-radius: 3px;
  transition: color var(--duration-fast) var(--ease-out-ui), background-color var(--duration-fast) var(--ease-out-ui); }
:where(body:not([data-app])) .intro .intro-action[hidden] { display: none; }
:where(body:not([data-app])) .intro .intro-action svg { width: 11px; height: 11px; }
@media (hover: hover) and (pointer: fine) { :where(body:not([data-app])) .intro .intro-action:hover { color: var(--ink); background: var(--wash); } }
@media (max-width: 1219px) { :where(body:not([data-app])) .intro .intro-action { right: var(--u); } }
@media (max-width: 613px) { :where(body:not([data-app])) .intro .intro-action { top: var(--u); } }

/* The hover tip on every page (core.js: tips) — the tools' small dark box, the same everywhere */
.sk-tip { position: fixed; z-index: 1000; left: -9999px; top: -9999px; max-width: min(320px, calc(100vw - 8px)); padding: 3px 6px; border-radius: 4px;
  background: var(--toast-bg, var(--ink)); color: var(--toast-ink, var(--paper)); font: 400 11px/16px var(--font-sans); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; pointer-events: none; opacity: 0; transition: opacity .08s; }
.sk-tip.visible { opacity: 1; }
