@openbooknetwork/kitap 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +103 -2
- package/bin/kitap.mjs +5 -0
- package/package.json +55 -4
- package/src/app.html +365 -0
- package/src/app.js +254 -0
- package/src/build.mjs +641 -0
- package/src/cli.mjs +41 -0
- package/src/common.js +62 -0
- package/src/deploy.mjs +52 -0
- package/src/dev.mjs +88 -0
- package/src/engine.mjs +33 -0
- package/src/folders.mjs +30 -0
- package/src/i18n/en.yaml +43 -0
- package/src/i18n/tr.yaml +43 -0
- package/src/i18n.mjs +67 -0
- package/src/image.mjs +214 -0
- package/src/index.html +183 -0
- package/src/index.mjs +15 -0
- package/src/pages.mjs +271 -0
- package/src/routes.mjs +23 -0
- package/src/schema.mjs +78 -0
- package/src/store.mjs +153 -0
- package/src/themes.yaml +27 -0
- package/src/translate.mjs +41 -0
- package/src/upload.mjs +114 -0
- package/src/util.mjs +19 -0
- package/src/vendor.mjs +67 -0
- package/src/worker.mjs +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Can Bascil
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Kitap
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The book under the Open Book Network sites. A book is a set of stories, a story is a run of pages, and a page is a
|
|
4
|
+
text beside a picture that changes as the reader steps. Kitap reads the stories, builds a static site with a page
|
|
5
|
+
per step in every language, and hands the picture to an engine: [harita](https://github.com/openbooknetwork/harita)
|
|
6
|
+
draws maps, [beden](https://github.com/openbooknetwork/beden) bodies, [makina](https://github.com/openbooknetwork/makina)
|
|
7
|
+
mechanisms, [hesap](https://github.com/openbooknetwork/hesap) algorithms.
|
|
8
|
+
|
|
9
|
+
## A content project
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
site.yaml the main page, the engine, the languages, the theme, the store
|
|
13
|
+
content/<story>/story.yaml id, title, summary, span, cover, languages, and the engine's fields under its key
|
|
14
|
+
content/<story>/pages/ a folder per page or group, numbered to order them
|
|
15
|
+
010-<group>/group.yaml the group's title
|
|
16
|
+
010-<group>/010-<page>/page.yaml, text/<lang>.md
|
|
17
|
+
content/<story>/shared/images/<name>-<six hex digits>/ image.yaml, the original, i18n/<lang>.yaml
|
|
18
|
+
content/<story>/shared/<kind>s/<id>/ the engine's items: battles, zones, models, ...
|
|
19
|
+
content/shared/images/, content/shared/<kind>s/ what several stories share
|
|
20
|
+
content/<story>/i18n/<lang>.yaml, i18n/<lang>.yaml the translations; the default language is inline
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
# site.yaml
|
|
25
|
+
title: Open Body Book
|
|
26
|
+
engine: beden
|
|
27
|
+
languages: [en, tr]
|
|
28
|
+
assets: { url: https://assets.example.org/, bucket: body-assets, endpoint: https://... }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
# page.yaml: the book's fields, then the engine's under its key
|
|
33
|
+
title: "The aorta"
|
|
34
|
+
date: "1628" # optional, shown above the title and in the contents
|
|
35
|
+
sources: ["Gray's Anatomy, ch. 58."]
|
|
36
|
+
beden:
|
|
37
|
+
look: aorta
|
|
38
|
+
parts: { highlight: [aorta] }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
A text is Markdown. `@image <id>` on a line of its own shows an image folder, and `[word](@id)` links a word to
|
|
42
|
+
something the engine shows on the page, a marker on a map or a part of a model.
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
kitap build [--strict] [--local-assets] [--only <story>] [--story <id>] [--out <dir>]
|
|
48
|
+
kitap check <story> [page id ...] [--places]
|
|
49
|
+
kitap dev [--story <id>] [--out <dir>] [--port <n>]
|
|
50
|
+
kitap i18n <lang> [--story <id>]
|
|
51
|
+
kitap image <file or url> <name> --caption <text> [--credit <text>] [--source <url>] [--story <id>]
|
|
52
|
+
kitap rehash | kitap upload [--story <id>] [--dry-run] | kitap deploy [--dry-run]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
An engine's own binary runs the same commands with its engine, `harita build` for instance, and adds its own.
|
|
56
|
+
`--strict` fails on a missing translation or an object the store lacks; `--local-assets` shows the files on this
|
|
57
|
+
machine before they are uploaded.
|
|
58
|
+
|
|
59
|
+
## An engine
|
|
60
|
+
|
|
61
|
+
An engine is a package whose default export is a module with an id and hooks. Kitap resolves `engine: <id>` of
|
|
62
|
+
site.yaml to `@openbooknetwork/<id>` in the project's packages, else to `plugins/engines/<id>.mjs`.
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
export default {
|
|
66
|
+
id: 'beden',
|
|
67
|
+
dir: PKG, // the package folder, where its libraries resolve from
|
|
68
|
+
schemas: { story: StoryBody, page: PageBody }, // zod objects for the fields under the engine's key
|
|
69
|
+
ui: new URL('./i18n/', import.meta.url), // strings laid over kitap's: hint, tab_map, map_data, ...
|
|
70
|
+
client: { script: URL, styles: URL, module: true, libraries: [[pkg, file]], imports: { three: [pkg, folder, file] } },
|
|
71
|
+
read: {
|
|
72
|
+
story(story, ctx) {}, // the story's own files; returns the state the other hooks read as ctx.state
|
|
73
|
+
folder(dir, scope, ctx) {}, // files in shared/ and in a page's folder
|
|
74
|
+
include(dir, scope, ctx) {}, // an include.yaml in the page tree: returns a page or group node, or null
|
|
75
|
+
prepare(ctx) {}, // after every folder is read, before the pages
|
|
76
|
+
page(fields, { dir, at, own, pid, scope, core }, ctx) {}, // returns { data, links, credits }
|
|
77
|
+
finish({ story, pages, texts, images, coverImage }, ctx) {}, // returns { images, warnings, folders }
|
|
78
|
+
},
|
|
79
|
+
bundle(content, ctx) {}, // returns { data, families, credits }: data goes to the viewport under the id
|
|
80
|
+
objects(content, ctx) {}, // returns { groups: { dir: [keys] }, ... }: what the store must hold
|
|
81
|
+
make({ stories, cache, out, log }) {}, // writes each object to out/<key>, for --local-assets and upload
|
|
82
|
+
site({ root, cache, strict, log, stories, fail }) {}, // checks across the stories before the build
|
|
83
|
+
describeObjects(keys) {}, // the objects in words, for messages
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`ctx` carries the project's readers (`readYaml`, `readJson`, `library`, `resolveImage`, `translatorFor`), the
|
|
88
|
+
story's translator `T`, `fail`, `attempt`, `warn`, the `scope` factory and the `storyScope`, and for an engine that
|
|
89
|
+
includes pages, `walk`, `pagesIn` and `readPage`. A `links` object answers the text's `[word](@id)` links:
|
|
90
|
+
`has(id)`, `attr(id)`, `missing(name, id)` and `names(lang)`.
|
|
91
|
+
|
|
92
|
+
The client script sets `window.KITAP_ENGINE = host => ({ show, lang, theme, resize, escape, point, highlight })`
|
|
93
|
+
and, as a module, dispatches `kitap-engine` once set. `host` carries the bundle `B`, the stage's elements under
|
|
94
|
+
`el`, `here()`, `lang()`, `L`, `T`, `css`, `pref` and the shell's other helpers. The engine owns everything inside
|
|
95
|
+
`el.map`, the legend and the view group; the shell owns the contents, the text, the tabs and the lightbox.
|
|
96
|
+
|
|
97
|
+
The test engine in `test/engines/plain.mjs` shows the least an engine needs.
|
|
98
|
+
|
|
99
|
+
## Work on it
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
npm install
|
|
103
|
+
npm test
|
|
104
|
+
```
|
package/bin/kitap.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// kitap build | check | dev | i18n | image | rehash | upload | deploy, run from a content project folder. An engine's
|
|
3
|
+
// own binary runs the same commands with its engine, and adds its own.
|
|
4
|
+
import { run } from '../src/cli.mjs';
|
|
5
|
+
await run(process.argv.slice(2));
|
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openbooknetwork/kitap",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The book under the Open Book Network sites: stories of pages with a text beside a picture an engine draws, built to a static site in every language",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"static-site",
|
|
7
|
+
"book",
|
|
8
|
+
"timeline",
|
|
9
|
+
"i18n"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "Can Bascil",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/openbooknetwork/kitap.git"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=22.12"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"kitap": "bin/kitap.mjs"
|
|
23
|
+
},
|
|
24
|
+
"exports": {
|
|
25
|
+
".": "./src/index.mjs",
|
|
26
|
+
"./schema": "./src/schema.mjs",
|
|
27
|
+
"./worker": "./src/worker.mjs",
|
|
28
|
+
"./package.json": "./package.json",
|
|
29
|
+
"./cli": "./src/cli.mjs",
|
|
30
|
+
"./src/*": "./src/*"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"bin",
|
|
34
|
+
"src"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "node --test test/*.test.mjs",
|
|
41
|
+
"prepublishOnly": "npm test"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@fontsource-variable/fraunces": "^5.3.0",
|
|
45
|
+
"@fontsource-variable/public-sans": "^5.3.0",
|
|
46
|
+
"aws4fetch": "^1.0.20",
|
|
47
|
+
"js-yaml": "^4.1.0",
|
|
48
|
+
"lucide-static": "^1.49.0",
|
|
49
|
+
"marked": "^15.0.0",
|
|
50
|
+
"sharp": "^0.35.5",
|
|
51
|
+
"zod": "^4.6.5"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/openbooknetwork/kitap#readme",
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/openbooknetwork/kitap/issues"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/app.html
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<!--__HEAD__-->
|
|
7
|
+
<link rel="stylesheet" href="../vendor/fonts.css">
|
|
8
|
+
<style>
|
|
9
|
+
:root{
|
|
10
|
+
/*__FAMILY_CSS__*/
|
|
11
|
+
--display:"Fraunces",Georgia,"Times New Roman",serif;
|
|
12
|
+
--body:"Public Sans","Helvetica Neue",Arial,sans-serif;
|
|
13
|
+
}
|
|
14
|
+
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){ /*__FAMILY_CSS_DARK__*/ } }
|
|
15
|
+
:root[data-theme="dark"]{ /*__FAMILY_CSS_DARK__*/ }
|
|
16
|
+
/*__THEMES_CSS__*/
|
|
17
|
+
/* one root size: 14px up to about 1500px wide, 19px from about 2600px, growing in between; everything else is in rem of it */
|
|
18
|
+
html{font-size:clamp(14px, 0.4545vw + 7.2px, 19px)}
|
|
19
|
+
html,body{height:100%}
|
|
20
|
+
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--body);font-size:1rem;line-height:1.5;
|
|
21
|
+
display:flex;flex-direction:column;gap:.857rem;padding-inline:1.143rem;padding-block:.857rem;box-sizing:border-box}
|
|
22
|
+
*{box-sizing:border-box}
|
|
23
|
+
[hidden]{display:none!important}
|
|
24
|
+
h1,h2,h3{font-family:var(--display);font-weight:500;margin:0;text-wrap:balance}
|
|
25
|
+
header{display:flex;align-items:center;gap:1.143rem;flex-wrap:wrap;border-bottom:1px solid var(--rule);padding-bottom:.714rem}
|
|
26
|
+
header h1{font-size:1.571rem}
|
|
27
|
+
header a.back{color:var(--ink-2);text-decoration:none;font-size:.929rem;display:inline-flex;align-items:center;gap:.286rem}
|
|
28
|
+
header a.back::before{content:"";width:0;height:0;border:5px solid transparent;border-inline-end:6px solid var(--ink-2)}
|
|
29
|
+
header a.back:hover{color:var(--ink)}
|
|
30
|
+
header a.back:focus-visible{outline:2px solid var(--accent)}
|
|
31
|
+
header .note{color:var(--ink-2);font-size:.857rem}
|
|
32
|
+
header .tools{margin-inline-start:auto;display:flex;align-items:center;gap:.571rem}
|
|
33
|
+
header a.src{color:var(--ink-2);font-size:.857rem;text-decoration:none;display:inline-flex;align-items:center;gap:.286rem;border:1px solid var(--rule);border-radius:4px;padding:.286rem .571rem}
|
|
34
|
+
header a.src svg{width:.929rem;height:.929rem}
|
|
35
|
+
header a.src:hover{color:var(--ink)}
|
|
36
|
+
header a.src:focus-visible{outline:2px solid var(--accent)}
|
|
37
|
+
/* controls: one bar, pinned under the text on wide screens, fixed to the screen bottom on phones; 44px targets for touch */
|
|
38
|
+
.ctl{display:grid;grid-template-columns:1fr auto 1fr;gap:.571rem .714rem;align-items:center;padding-top:.714rem;border-top:1px solid var(--rule);background:var(--paper)}
|
|
39
|
+
.ctl button{min-height:max(3.143rem,3.15rem);background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:6px;padding:0 1rem;font:inherit;font-size:1.071rem;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:.429rem}
|
|
40
|
+
.ctl button svg{width:1.286rem;height:1.286rem}
|
|
41
|
+
:root[dir="rtl"] :is(#prev,#next,.bc .pic .nav button) svg{transform:scaleX(-1)} /* the chevrons point the way the text reads */
|
|
42
|
+
.ctl button:disabled{opacity:.35;cursor:default}
|
|
43
|
+
.ctl button:focus-visible{outline:2px solid var(--accent)}
|
|
44
|
+
.ctl #stepno{font-variant-numeric:tabular-nums;color:var(--ink-2);font-size:.929rem;text-align:center;white-space:nowrap}
|
|
45
|
+
/* the language, beside the settings on every screen, so a reader who cannot read the current one still finds it */
|
|
46
|
+
.langpick{display:inline-flex;align-items:center;gap:.143rem;background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:4px;padding-inline-start:.571rem;font-size:.857rem}
|
|
47
|
+
.langpick svg{width:.929rem;height:.929rem;flex:none}
|
|
48
|
+
.langpick select{background:var(--panel);color:inherit;border:0;border-radius:4px;font:inherit;padding:.286rem .429rem;cursor:pointer}
|
|
49
|
+
.langpick:focus-within{outline:2px solid var(--accent)}
|
|
50
|
+
.langpick select:focus{outline:none}
|
|
51
|
+
/* settings: the layout and the theme, in a panel under its button at the header's end on every screen */
|
|
52
|
+
.prefs{position:relative}
|
|
53
|
+
#settings-btn{background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:4px;padding:.286rem .571rem;font:inherit;font-size:.857rem;cursor:pointer;display:inline-flex;align-items:center;gap:.286rem}
|
|
54
|
+
#settings-btn svg{width:.929rem;height:.929rem}
|
|
55
|
+
#settings-btn:focus-visible{outline:2px solid var(--accent)}
|
|
56
|
+
.settings{position:absolute;top:calc(100% + .429rem);inset-inline-end:0;z-index:25;width:min(24rem,calc(100vw - 2.286rem));display:grid;gap:.857rem;
|
|
57
|
+
background:var(--panel);border:1px solid var(--rule);border-radius:6px;padding:.857rem;box-shadow:0 4px 16px rgba(0,0,0,.25)}
|
|
58
|
+
.settings[hidden]{display:none}
|
|
59
|
+
.settings .group{display:grid;gap:.357rem}
|
|
60
|
+
.settings .group > span{font-size:.786rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}
|
|
61
|
+
.settings select{width:100%;min-height:max(2.857rem,2.85rem);background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:6px;padding:0 .571rem;font:inherit;cursor:pointer}
|
|
62
|
+
.settings select:focus-visible,.layouts button:focus-visible{outline:2px solid var(--accent)}
|
|
63
|
+
.layouts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.429rem}
|
|
64
|
+
.layouts button{display:grid;justify-items:center;align-content:start;gap:.286rem;padding:.429rem;font:inherit;font-size:.786rem;line-height:1.25;text-align:center;
|
|
65
|
+
background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:6px;cursor:pointer}
|
|
66
|
+
.layouts button svg{width:100%;height:auto;max-width:4.6rem}
|
|
67
|
+
.layouts button[aria-checked="true"]{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
|
|
68
|
+
/* the layout: the map in the middle, or the chronology and the text together on the screen's left or right edge,
|
|
69
|
+
for a large touch screen where the reader stands at one side; left and right hold in any writing direction */
|
|
70
|
+
@media (min-width:901px){
|
|
71
|
+
:root[data-layout="left"] .app{grid-template-columns:clamp(220px,15vw,300px) clamp(320px,23vw,520px) minmax(0,1fr)}
|
|
72
|
+
:root[data-layout="right"] .app{grid-template-columns:minmax(0,1fr) clamp(320px,23vw,520px) clamp(220px,15vw,300px)}
|
|
73
|
+
:root[data-layout="left"] nav.pages,:root[data-layout="right"] .stage{grid-area:1/1}
|
|
74
|
+
:root[data-layout="left"] aside.story,:root[data-layout="right"] aside.story{grid-area:1/2}
|
|
75
|
+
:root[data-layout="left"] .stage,:root[data-layout="right"] nav.pages{grid-area:1/3}
|
|
76
|
+
:root:is([data-layout="left"],[data-layout="right"]) .app{direction:ltr}
|
|
77
|
+
:root[dir="rtl"]:is([data-layout="left"],[data-layout="right"]) .app > *{direction:rtl}
|
|
78
|
+
}
|
|
79
|
+
.app{display:grid;grid-template-columns:clamp(220px,15vw,300px) minmax(0,1fr) clamp(320px,23vw,520px);gap:1.143rem;flex:1;min-height:0}
|
|
80
|
+
nav.pages{overflow:auto;min-height:0}
|
|
81
|
+
.where,nav.pages .sheet-head,.ctl .tabs{display:none}
|
|
82
|
+
.peek,.legend-btn{display:none}
|
|
83
|
+
aside.story{display:grid;grid-template-rows:minmax(0,1fr) auto;min-height:0}
|
|
84
|
+
aside.story .text{overflow:auto;min-height:0;padding-inline-end:.286rem}
|
|
85
|
+
nav.pages .toggle-all{display:block;width:auto;margin-block:0 .429rem;margin-inline:auto 0;background:none;border:1px solid var(--rule);border-radius:4px;color:var(--ink-2);font:inherit;font-size:.786rem;padding:3px .571rem;cursor:pointer}
|
|
86
|
+
nav.pages .toggle-all:hover{color:var(--ink)}
|
|
87
|
+
nav.pages .toggle-all:focus-visible{outline:2px solid var(--accent)}
|
|
88
|
+
nav.pages summary{font-family:var(--display);font-size:.929rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-2);margin:.571rem 0 2px;padding-inline-start:calc(var(--depth,0)*.857rem);cursor:pointer;list-style:none;display:flex;align-items:center;gap:.429rem}
|
|
89
|
+
nav.pages summary::before{content:"";width:0;height:0;border:4px solid transparent;border-inline-start:5px solid var(--ink-2);transition:transform .15s}
|
|
90
|
+
nav.pages details[open]>summary::before{transform:rotate(90deg)}
|
|
91
|
+
:root[dir="rtl"] nav.pages details[open]>summary::before{transform:rotate(-90deg)}
|
|
92
|
+
nav.pages summary:focus-visible{outline:2px solid var(--accent)}
|
|
93
|
+
nav.pages button,nav.pages a.pg{display:block;width:100%;text-align:start;background:none;border:0;border-inline-start:2px solid transparent;color:var(--ink);text-decoration:none;
|
|
94
|
+
font:inherit;padding-block:.429rem;padding-inline:calc(.714rem + var(--depth,0)*.857rem) .714rem;cursor:pointer;line-height:1.3}
|
|
95
|
+
nav.pages .pg .d{display:block;font-size:.786rem;color:var(--ink-2);font-variant-numeric:tabular-nums}
|
|
96
|
+
nav.pages .pg[aria-current="page"]{border-inline-start-color:var(--accent);background:var(--panel)}
|
|
97
|
+
nav.pages button:focus-visible,nav.pages a.pg:focus-visible{outline:2px solid var(--accent)}
|
|
98
|
+
.stage{position:relative;min-height:30rem;border:1px solid var(--rule);background:var(--sea);overflow:hidden}
|
|
99
|
+
.map{position:absolute;inset:0;width:100%;height:100%}
|
|
100
|
+
.legend{position:absolute;inset-inline-start:.714rem;bottom:.714rem;background:var(--panel);border:1px solid var(--rule);padding:.571rem .714rem;font-size:.786rem;
|
|
101
|
+
display:grid;gap:.286rem;max-width:15.714rem}
|
|
102
|
+
.legend .lg{display:flex;align-items:center;gap:.429rem;background:none;border:0;border-radius:3px;padding:.143rem .214rem;margin:-.143rem -.214rem;
|
|
103
|
+
color:inherit;font:inherit;text-align:start;cursor:pointer}
|
|
104
|
+
.legend .lg:hover,.legend .lg[aria-pressed="true"]{background:var(--paper-2)}
|
|
105
|
+
.legend .lg:focus-visible{outline:2px solid var(--accent)}
|
|
106
|
+
/* the name of the highlighted zone or route: beside the pointer, or pinned at the top of the map after a tap */
|
|
107
|
+
.hl-label{position:absolute;z-index:4;pointer-events:none;background:var(--ink);color:var(--paper);font-size:.857rem;line-height:1.3;
|
|
108
|
+
padding:.286rem .571rem;border-radius:4px;max-width:18rem;box-shadow:0 1px 4px rgba(0,0,0,.3)}
|
|
109
|
+
.hl-label.pinned{top:calc(.714rem + max(40px,2.857rem) + .429rem);left:50%;transform:translateX(-50%);max-width:calc(100% - 1.143rem);text-align:center} /* below the 2D | 3D switch */
|
|
110
|
+
.legend i{width:1rem;height:.714rem;display:inline-block;border:1px solid var(--ink-2);flex:none}
|
|
111
|
+
.legend .r{width:1rem;height:0;border:0;border-top:2px solid var(--rc);position:relative}
|
|
112
|
+
.legend .r.arrow::after{content:"";position:absolute;inset-inline-end:-2px;top:-5px;border:4px solid transparent;border-inline-start:5px solid var(--rc)}
|
|
113
|
+
.legend .r.dashed{border-top-style:dashed}
|
|
114
|
+
.credit{position:absolute;inset-inline-end:.714rem;bottom:.714rem;background:var(--panel);border:1px solid var(--rule);padding:3px .571rem;font-size:.786rem;color:var(--ink-2);pointer-events:auto;
|
|
115
|
+
display:flex;align-items:center;gap:.571rem}
|
|
116
|
+
.credit a{color:inherit}
|
|
117
|
+
.credit :is(#mapdata,#othersrc){background:none;border:0;border-inline-start:1px solid var(--rule);padding:0;padding-inline-start:.571rem;font:inherit;color:inherit;text-decoration:underline;cursor:pointer}
|
|
118
|
+
.credit :is(#mapdata,#othersrc) svg{display:none;width:1.143rem;height:1.143rem}
|
|
119
|
+
.credit :is(#mapdata,#othersrc):focus-visible{outline:2px solid var(--accent)}
|
|
120
|
+
/* the sources of the borders and the relief, opened from the credit */
|
|
121
|
+
.credits{position:absolute;inset-inline-end:-1px;bottom:calc(100% + .429rem);width:max-content;max-width:min(28rem,calc(100vw - 3rem));background:var(--panel);
|
|
122
|
+
border:1px solid var(--rule);border-radius:4px;padding:.571rem .714rem;box-shadow:0 2px 10px rgba(0,0,0,.2);line-height:1.4}
|
|
123
|
+
.credits h4{margin:0 0 .286rem;font-size:.786rem;letter-spacing:.06em;text-transform:uppercase}
|
|
124
|
+
.credits ul{margin:0;padding-inline-start:1rem;display:grid;gap:.286rem}
|
|
125
|
+
.credit #othersrc[hidden]{display:none}
|
|
126
|
+
.credits .made{display:none;margin:0 0 .429rem}
|
|
127
|
+
/* map tools: the 2D | 3D switch at the top of the map, joined on phones by the Legend button */
|
|
128
|
+
.maptools{position:absolute;top:.714rem;inset-inline-end:.714rem;z-index:3;display:flex;gap:.429rem;align-items:flex-start}
|
|
129
|
+
.view{display:flex;background:var(--panel);border:1px solid var(--rule);border-radius:6px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.15)}
|
|
130
|
+
.view button{min-width:max(44px,3.143rem);min-height:calc(max(40px,2.857rem) - 2px);padding:0 .571rem;border:0;background:none;color:var(--ink);font:inherit;font-size:.929rem;font-weight:600;cursor:pointer}
|
|
131
|
+
.view button + button{border-inline-start:1px solid var(--rule)}
|
|
132
|
+
.view button[aria-pressed="true"]{background:var(--ink);color:var(--paper)}
|
|
133
|
+
.view button:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
|
134
|
+
.fail{position:absolute;inset:0;display:grid;place-items:center;padding:1.714rem;text-align:center;color:var(--ink-2)}
|
|
135
|
+
aside.story .date{font-size:.857rem;letter-spacing:.06em;text-transform:uppercase;color:var(--accent);font-variant-numeric:tabular-nums}
|
|
136
|
+
aside.story h2{font-size:1.714rem;margin:.286rem 0 .714rem;line-height:1.15}
|
|
137
|
+
aside.story p{margin:0 0 .714rem;max-width:62ch}
|
|
138
|
+
aside.story .start{display:inline-flex;align-items:center;gap:.286rem;margin-top:.429rem;background:var(--ink);color:var(--paper);text-decoration:none;padding:.5rem 1rem;border-radius:4px;font-weight:600}
|
|
139
|
+
aside.story .start svg{width:1rem;height:1rem}
|
|
140
|
+
aside.story .steps{display:flex;justify-content:space-between;gap:.857rem;margin-top:1.286rem}
|
|
141
|
+
aside.story h3{font-size:.929rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2);margin-top:1.286rem;border-top:1px solid var(--rule);padding-top:.714rem}
|
|
142
|
+
aside.story ol{padding-inline-start:1.286rem;margin:.429rem 0;color:var(--ink-2);font-size:.857rem}
|
|
143
|
+
aside.story li{margin-bottom:.286rem}
|
|
144
|
+
aside.story figure{margin:1rem 0;max-width:62ch}
|
|
145
|
+
aside.story figure img{display:block;width:100%;border:1px solid var(--rule);cursor:zoom-in}
|
|
146
|
+
aside.story figcaption{font-size:.857rem;color:var(--ink-2);margin-top:.429rem;line-height:1.4}
|
|
147
|
+
/* a place the text names: pointing at it pops its marker on the map, pointing at the marker tints it */
|
|
148
|
+
aside.story .place{text-decoration:underline dotted var(--c,var(--ink-2));text-decoration-thickness:1.5px;text-underline-offset:.2em;border-radius:2px;cursor:default}
|
|
149
|
+
aside.story .place:hover,aside.story .place:focus-visible,aside.story .place.on{text-decoration-style:solid;background:color-mix(in srgb,var(--c,var(--accent)) 18%,transparent);outline:none}
|
|
150
|
+
aside.story figcaption small{display:block;font-size:.786rem;color:var(--ink-2);opacity:.8}
|
|
151
|
+
/* markers */
|
|
152
|
+
.mk{display:flex;flex-direction:column;align-items:center;gap:2px;pointer-events:none;transform:translate(0,-50%)}
|
|
153
|
+
.mk .wrap{position:relative;display:flex;flex-direction:column;align-items:center;transform-origin:50% 100%;transition:transform .15s}
|
|
154
|
+
.mk.named .wrap{pointer-events:auto}
|
|
155
|
+
/* the marker of the place the reader points at in the text: larger, above the others, with a ring in its colour */
|
|
156
|
+
.mk.pop{z-index:1}
|
|
157
|
+
.mk.pop .wrap{transform:scale(1.25)}
|
|
158
|
+
.mk.pop .ph:hover{transform:none}
|
|
159
|
+
.mk.pop .wrap>.ic:not(.badge),.mk.pop .ph{box-shadow:0 0 0 .286rem color-mix(in srgb,var(--c,var(--accent)) 40%,transparent),0 1px 4px rgba(0,0,0,.35)}
|
|
160
|
+
.mk .ic{width:2rem;height:2rem;border-radius:50%;background:var(--panel);border:2px solid var(--c,var(--accent));color:var(--c,var(--accent));
|
|
161
|
+
display:grid;place-items:center;box-shadow:0 1px 3px rgba(0,0,0,.3)}
|
|
162
|
+
.mk .ic svg{width:1.143rem;height:1.143rem}
|
|
163
|
+
.mk.battle{pointer-events:auto;cursor:pointer}
|
|
164
|
+
.mk.battle .ic{width:2.714rem;height:2.714rem;background:var(--accent);color:var(--paper);border-color:var(--paper);box-shadow:0 1px 5px rgba(0,0,0,.4)}
|
|
165
|
+
.mk.battle .ic svg{width:1.571rem;height:1.571rem}
|
|
166
|
+
.mk.battle b{font-size:.929rem}
|
|
167
|
+
.mk.battle:hover .ic{transform:scale(1.08)}
|
|
168
|
+
.mk .ph{width:max(3.143rem,3.15rem);height:max(3.143rem,3.15rem);border-radius:50%;object-fit:cover;border:2px solid var(--c,var(--accent));background:var(--panel);
|
|
169
|
+
box-shadow:0 1px 4px rgba(0,0,0,.35);pointer-events:auto;cursor:zoom-in;display:block}
|
|
170
|
+
.mk .ph:hover{transform:scale(1.08)}
|
|
171
|
+
.mk .ph:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
172
|
+
.mk .ic.badge{position:absolute;right:-0.429rem;bottom:-2px;width:1.429rem;height:1.429rem;border-width:1.5px;box-shadow:none}
|
|
173
|
+
.mk .ic.badge svg{width:.786rem;height:.786rem}
|
|
174
|
+
.mk b{font-family:var(--display);font-weight:700;font-size:.857rem;color:var(--ink);background:var(--paper);padding:0 .286rem;border-radius:2px;white-space:nowrap}
|
|
175
|
+
.mk small{font-size:.714rem;color:var(--ink-2);background:var(--paper);padding:0 .286rem;border-radius:2px;white-space:nowrap}
|
|
176
|
+
.maplibregl-marker{opacity:0;transition:opacity .6s}
|
|
177
|
+
/* the number of a leg, when one family has more than one route on the page: on the map at the middle of the leg, and in the legend */
|
|
178
|
+
.legno{min-width:1.286rem;height:1.286rem;padding:0 .214rem;border-radius:999px;display:inline-grid;place-items:center;flex:none;background:var(--rc);color:var(--paper);
|
|
179
|
+
border:2px solid var(--paper);font-size:.714rem;font-weight:600;line-height:1;font-variant-numeric:tabular-nums;pointer-events:none}
|
|
180
|
+
.maplibregl-marker.in{opacity:1}
|
|
181
|
+
/* battle card */
|
|
182
|
+
.maplibregl-popup{position:absolute;top:0;left:0;display:flex;will-change:transform;pointer-events:none;z-index:4}
|
|
183
|
+
.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}
|
|
184
|
+
.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}
|
|
185
|
+
.maplibregl-popup-anchor-left{flex-direction:row}
|
|
186
|
+
.maplibregl-popup-anchor-right{flex-direction:row-reverse}
|
|
187
|
+
.maplibregl-popup-tip{width:0;height:0;border:10px solid transparent;z-index:1;align-self:center}
|
|
188
|
+
.maplibregl-popup-anchor-top .maplibregl-popup-tip,.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{border-top:none;border-bottom-color:var(--panel)}
|
|
189
|
+
.maplibregl-popup-anchor-bottom .maplibregl-popup-tip,.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{border-bottom:none;border-top-color:var(--panel)}
|
|
190
|
+
.maplibregl-popup-anchor-left .maplibregl-popup-tip{border-left:none;border-right-color:var(--panel)}
|
|
191
|
+
.maplibregl-popup-anchor-right .maplibregl-popup-tip{border-right:none;border-left-color:var(--panel)}
|
|
192
|
+
.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;margin-left:1rem}
|
|
193
|
+
.maplibregl-popup-anchor-top-right .maplibregl-popup-tip,.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;margin-right:1rem}
|
|
194
|
+
.maplibregl-popup-content{position:relative;pointer-events:auto;background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:4px;box-shadow:0 4px 18px rgba(0,0,0,.3);padding:0;width:min(28.5rem,calc(100vw - 3.429rem))}
|
|
195
|
+
.maplibregl-popup-close-button{position:absolute;inset-inline-end:.429rem;top:.429rem;width:1.857rem;height:1.857rem;border:0;border-radius:3px;background:none;color:var(--ink-2);font:18px/1 var(--body);cursor:pointer}
|
|
196
|
+
.maplibregl-popup-close-button:hover{color:var(--ink);background:var(--paper-2)}
|
|
197
|
+
.maplibregl-popup-close-button:focus-visible{outline:2px solid var(--accent)}
|
|
198
|
+
.bc{font-size:.929rem;line-height:1.4;max-height:min(40rem,70vh);overflow:auto}
|
|
199
|
+
.bc header{display:flex;gap:.714rem;align-items:center;padding-block:.857rem .714rem;padding-inline:1rem 2.571rem;border-bottom:1px solid var(--rule)}
|
|
200
|
+
.bc header .ic{flex:none;width:2.143rem;height:2.143rem;border-radius:50%;background:var(--accent);color:var(--paper);display:grid;place-items:center}
|
|
201
|
+
.bc header .ic svg{width:1.286rem;height:1.286rem}
|
|
202
|
+
.bc header h4{font-family:var(--display);font-weight:500;font-size:1.214rem;margin:0;line-height:1.2}
|
|
203
|
+
.bc header small{display:block;color:var(--ink-2);font-size:.857rem}
|
|
204
|
+
.bc .result{padding:.571rem 1rem;border-bottom:1px solid var(--rule);display:flex;gap:.571rem;align-items:baseline}
|
|
205
|
+
.bc .result b{font-size:.786rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}
|
|
206
|
+
.bc .sides{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:0;border-bottom:1px solid var(--rule)}
|
|
207
|
+
.bc .side{padding:.714rem 1rem;border-inline-start:3px solid var(--c,var(--rule))}
|
|
208
|
+
.bc .side + .side{border-top:0;box-shadow:-1px 0 0 var(--rule)}
|
|
209
|
+
:root[dir="rtl"] .bc .side + .side{box-shadow:1px 0 0 var(--rule)}
|
|
210
|
+
.bc .side h5{margin:0 0 .429rem;font-size:.929rem;font-weight:600}
|
|
211
|
+
.bc .side dl{margin:0;display:grid;gap:.286rem}
|
|
212
|
+
.bc .side dt{font-size:.786rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}
|
|
213
|
+
.bc .side dd{margin:0}
|
|
214
|
+
.bc .side ul{margin:0;padding-inline-start:1.143rem}
|
|
215
|
+
.bc .pic{margin:0;border-bottom:1px solid var(--rule)}
|
|
216
|
+
.bc .pic .slide{height:15rem;background:var(--paper-2)}
|
|
217
|
+
.bc .pic img{display:block;width:100%;height:100%;object-fit:contain;cursor:zoom-in}
|
|
218
|
+
.bc .pic figcaption{padding:.429rem 1rem;font-size:.786rem;color:var(--ink-2);line-height:1.3;min-height:2.9em}
|
|
219
|
+
.bc .pic .nav{display:flex;align-items:center;justify-content:center;gap:.714rem;padding:2px 0 .429rem;font-size:.786rem;color:var(--ink-2);font-variant-numeric:tabular-nums}
|
|
220
|
+
.bc .pic .nav button{width:max(2.571rem,2.6rem);height:max(2.571rem,2.6rem);border:1px solid var(--rule);border-radius:50%;background:var(--paper);color:var(--ink);display:grid;place-items:center;cursor:pointer;padding:0}
|
|
221
|
+
.bc .pic .nav button svg{width:.929rem;height:.929rem}
|
|
222
|
+
.bc .pic .nav button:hover{background:var(--paper-2)}
|
|
223
|
+
.bc .pic .nav button:focus-visible{outline:2px solid var(--accent)}
|
|
224
|
+
.bc footer{display:flex;justify-content:flex-end;padding:.571rem 1rem}
|
|
225
|
+
.bc footer a{color:var(--ink-2);font-size:.857rem;display:inline-flex;gap:.286rem;align-items:center;text-decoration:none}
|
|
226
|
+
.bc footer a svg{width:.929rem;height:.929rem}
|
|
227
|
+
.bc footer a:hover{color:var(--ink)}
|
|
228
|
+
@media (max-width:640px){ .bc .sides{grid-template-columns:1fr} .bc .side + .side{box-shadow:none;border-top:1px solid var(--rule)} }
|
|
229
|
+
/* lightbox */
|
|
230
|
+
.lightbox{position:absolute;inset:0;background:rgba(20,18,14,.78);display:grid;place-items:center;padding:1.714rem;z-index:5}
|
|
231
|
+
.lightbox figure{margin:0;max-width:min(45.714rem,100%);max-height:100%;display:flex;flex-direction:column;gap:.571rem}
|
|
232
|
+
.lightbox img{max-width:100%;max-height:70vh;object-fit:contain;border:4px solid var(--panel);background:var(--panel)}
|
|
233
|
+
.lightbox figcaption{color:#efe9da;font-size:.929rem;line-height:1.4}
|
|
234
|
+
.lightbox figcaption small{display:block;color:#b8b09c;font-size:.786rem}
|
|
235
|
+
.lightbox button{position:absolute;top:.714rem;inset-inline-end:.714rem;background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:4px;padding:.286rem .714rem;font:inherit;cursor:pointer}
|
|
236
|
+
.lightbox button:focus-visible{outline:2px solid var(--accent)}
|
|
237
|
+
/* minimal maplibre css */
|
|
238
|
+
.maplibregl-map{font:12px/20px sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0 0 0/0)}
|
|
239
|
+
.maplibregl-canvas{position:absolute;left:0;top:0}
|
|
240
|
+
.maplibregl-canvas-container.maplibregl-interactive{cursor:grab;user-select:none}
|
|
241
|
+
.maplibregl-canvas-container.maplibregl-interactive:active{cursor:grabbing}
|
|
242
|
+
.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}
|
|
243
|
+
.maplibregl-marker{position:absolute;top:0;left:0;will-change:transform}
|
|
244
|
+
.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-bottom-left{display:none}
|
|
245
|
+
@media (max-width:900px){
|
|
246
|
+
/* one screen, no page scroll: header, where-you-are line, then the map or the text, then the bar */
|
|
247
|
+
html,body{height:100%}
|
|
248
|
+
body{height:100dvh;overflow:hidden;padding-bottom:calc(7.1rem + env(safe-area-inset-bottom, 0px))}
|
|
249
|
+
header .note{display:none}
|
|
250
|
+
.app{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1fr);min-height:0}
|
|
251
|
+
.stage{grid-row:2;grid-column:1;min-height:0}
|
|
252
|
+
aside.story{display:contents}
|
|
253
|
+
aside.story .text{grid-row:2;grid-column:1;z-index:2;background:var(--paper);overflow:auto;padding:.571rem .143rem 1.143rem}
|
|
254
|
+
body[data-tab="map"] aside.story .text{display:none}
|
|
255
|
+
/* in the text view the map's own overlays step aside; they sit above the text in the stacking order otherwise */
|
|
256
|
+
body[data-tab="text"] :is(.peek,.maptools,.legend,.credit,.maplibregl-popup,.hl-label){display:none}
|
|
257
|
+
.hl-label.pinned{top:calc(.571rem + max(40px,2.857rem) + .429rem)} /* below the map tools and the credit */
|
|
258
|
+
/* the peek card: the first two lines of the page's text over the bottom of the map; a tap opens the text */
|
|
259
|
+
.peek{display:block;position:absolute;inset-inline:.571rem;bottom:.571rem;z-index:3;background:var(--panel);border:1px solid var(--rule);border-radius:8px;
|
|
260
|
+
box-shadow:0 2px 10px rgba(0,0,0,.25);padding-block:.714rem;padding-inline:.857rem 2.857rem;cursor:pointer}
|
|
261
|
+
.peek[hidden]{display:none}
|
|
262
|
+
/* the text and the map take turns, so a place reads as plain text */
|
|
263
|
+
aside.story .place,aside.story .place:is(:hover,:focus-visible,.on){text-decoration:none;background:none}
|
|
264
|
+
.peek p{margin:0;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;line-height:1.45}
|
|
265
|
+
.peek .more{display:inline-block;margin-top:.286rem;color:var(--accent);font-weight:600;font-size:.929rem}
|
|
266
|
+
.peek .close{position:absolute;top:.286rem;inset-inline-end:.286rem;width:max(44px,3.143rem);height:max(44px,3.143rem);border:0;background:none;color:var(--ink-2);font-size:1.429rem;line-height:1;cursor:pointer}
|
|
267
|
+
.peek:focus-visible,.peek .close:focus-visible{outline:2px solid var(--accent)}
|
|
268
|
+
/* the legend folds into a button at the top of the map with the 2D | 3D switch beside it; the credit shrinks to a button */
|
|
269
|
+
.maptools{top:.571rem;inset-inline:.571rem auto}
|
|
270
|
+
.legend-btn{display:inline-flex;min-height:max(40px,2.857rem);align-items:center;padding:0 .857rem;
|
|
271
|
+
background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:6px;font:inherit;font-size:.929rem;cursor:pointer}
|
|
272
|
+
.legend-btn[hidden]{display:none}
|
|
273
|
+
.legend{top:calc(.571rem + max(40px,2.857rem) + .429rem);bottom:auto;inset-inline-start:.571rem;z-index:3;max-width:calc(100% - 1.143rem)}
|
|
274
|
+
body:not(.legend-open) .legend{display:none}
|
|
275
|
+
.credit{top:.571rem;bottom:auto;inset-inline-end:.571rem;padding:0;border:0;background:none}
|
|
276
|
+
.credit #made{display:none}
|
|
277
|
+
.credit :is(#mapdata,#othersrc){display:grid;place-items:center;width:max(44px,3.143rem);height:max(40px,2.857rem);padding:0;background:var(--panel);border:1px solid var(--rule);border-radius:6px;color:var(--ink-2)}
|
|
278
|
+
.credit :is(#mapdata,#othersrc) svg{display:block} .credit :is(#mapdata,#othersrc) span{display:none}
|
|
279
|
+
.credits{top:calc(100% + .429rem);bottom:auto;inset-inline-end:0}
|
|
280
|
+
.credits .made{display:block}
|
|
281
|
+
.lightbox{position:fixed;z-index:30}
|
|
282
|
+
/* the bar: Prev, the Map | Text switch, Next; then the page count. A phone has one layout. */
|
|
283
|
+
.ctl{grid-template-areas:"prev tabs next" ". no .";grid-template-columns:1fr auto 1fr}
|
|
284
|
+
.ctl #prev{grid-area:prev} .ctl #next{grid-area:next} .ctl #stepno{grid-area:no}
|
|
285
|
+
.settings .layout-group{display:none}
|
|
286
|
+
.ctl .tabs{grid-area:tabs;display:flex;border:1px solid var(--rule);border-radius:6px;overflow:hidden}
|
|
287
|
+
.ctl .tabs button{border:0;border-radius:0;min-width:4.6rem;padding:0 .714rem}
|
|
288
|
+
.ctl .tabs button + button{border-inline-start:1px solid var(--rule)}
|
|
289
|
+
.ctl .tabs button[aria-selected="true"]{background:var(--ink);color:var(--paper)}
|
|
290
|
+
.ctl{position:fixed;inset-inline:0;bottom:0;z-index:6;padding:.571rem 1.143rem calc(.571rem + env(safe-area-inset-bottom, 0px));border-top:1px solid var(--rule);box-shadow:0 -2px 10px rgba(0,0,0,.12)}
|
|
291
|
+
/* the chronology gives way to one line: where in the story you are, and a button for the full contents */
|
|
292
|
+
.where{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.143rem .857rem;align-items:center;padding-bottom:.571rem;border-bottom:1px solid var(--rule)}
|
|
293
|
+
.where .crumbs{grid-column:1;font-size:.786rem;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
294
|
+
.where .here{grid-column:1;font-weight:600;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
295
|
+
.where .here small{font-weight:400;color:var(--ink-2);margin-inline-end:.429rem;font-variant-numeric:tabular-nums}
|
|
296
|
+
.where button{grid-column:2;grid-row:1/3;min-height:max(44px,3.143rem);background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:6px;padding:0 .857rem;font:inherit;cursor:pointer;display:inline-flex;align-items:center;gap:.429rem}
|
|
297
|
+
.where button svg{width:1.143rem;height:1.143rem}
|
|
298
|
+
.where button:focus-visible{outline:2px solid var(--accent)}
|
|
299
|
+
nav.pages{display:none}
|
|
300
|
+
body.sheet{overflow:hidden}
|
|
301
|
+
body.sheet nav.pages{display:block;position:fixed;inset:0;z-index:20;background:var(--paper);overflow:auto;
|
|
302
|
+
padding:calc(.857rem + env(safe-area-inset-top, 0px)) 1.143rem calc(1.143rem + env(safe-area-inset-bottom, 0px))}
|
|
303
|
+
body.sheet nav.pages .sheet-head{display:flex;align-items:center;justify-content:space-between;gap:.857rem;margin-bottom:.571rem;
|
|
304
|
+
position:sticky;top:calc(-.857rem - env(safe-area-inset-top, 0px));z-index:1;background:var(--paper);padding-block:.571rem;border-bottom:1px solid var(--rule)} /* title and Close stay put while the list scrolls */
|
|
305
|
+
nav.pages .sheet-head h2{font-size:1.429rem}
|
|
306
|
+
nav.pages .sheet-close{width:auto;flex:none;text-align:center;min-height:max(44px,3.143rem);background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:6px;padding:0 .857rem;font:inherit;cursor:pointer}
|
|
307
|
+
nav.pages button,nav.pages a.pg{min-height:max(44px,3.143rem)}
|
|
308
|
+
}
|
|
309
|
+
@media (prefers-reduced-motion: reduce){ *{transition:none!important} .mk.pop .wrap{transform:none} }
|
|
310
|
+
</style>
|
|
311
|
+
<!-- the saved layout before the first paint, so the page does not move once the script runs -->
|
|
312
|
+
<script>try { const l = localStorage.getItem('layout'); if (l === 'left' || l === 'right') document.documentElement.dataset.layout = l; } catch {}</script>
|
|
313
|
+
</head>
|
|
314
|
+
<body>
|
|
315
|
+
|
|
316
|
+
<header>
|
|
317
|
+
<a class="back" id="back" href="../"></a>
|
|
318
|
+
<h1 id="title"></h1>
|
|
319
|
+
<span class="note" id="hint"></span>
|
|
320
|
+
<div class="tools">
|
|
321
|
+
<a class="src" id="src" target="_blank" rel="noopener" hidden></a>
|
|
322
|
+
<label class="langpick" id="langpick"><span id="lang-icon" aria-hidden="true"></span><select id="lang"></select></label>
|
|
323
|
+
<div class="prefs">
|
|
324
|
+
<button type="button" id="settings-btn" aria-expanded="false" aria-controls="settings"></button>
|
|
325
|
+
<div class="settings" id="settings" hidden>
|
|
326
|
+
<div class="group layout-group"><span id="layout-label"></span><div class="layouts" id="layouts" role="radiogroup"></div></div>
|
|
327
|
+
<label class="group"><span id="theme-label"></span><select id="theme"></select></label>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
</header>
|
|
332
|
+
|
|
333
|
+
<main class="app">
|
|
334
|
+
<div class="where" id="where"><div class="crumbs" id="crumbs"></div><div class="here" id="here"></div><button type="button" id="contents"></button></div>
|
|
335
|
+
<nav class="pages" id="nav"></nav>
|
|
336
|
+
<section class="stage">
|
|
337
|
+
<div id="map" class="map"></div>
|
|
338
|
+
<div class="maptools"><button type="button" class="legend-btn" id="legend-btn"></button><div class="view" id="view" role="group"><button type="button" data-view="2d" aria-pressed="true"></button><button type="button" data-view="3d" aria-pressed="false"></button></div></div>
|
|
339
|
+
<div class="legend" id="legend"></div>
|
|
340
|
+
<div class="hl-label" id="hl-label" hidden></div>
|
|
341
|
+
<div class="credit" id="credit"><span id="made"></span><button type="button" id="mapdata" aria-expanded="false" aria-controls="credits"></button><div class="credits" id="credits" hidden></div><button type="button" id="othersrc" aria-expanded="false" aria-controls="othercredits" hidden></button><div class="credits" id="othercredits" hidden></div></div>
|
|
342
|
+
<div class="peek" id="peek" role="button" tabindex="0"><p id="peek-text"></p><span class="more" id="peek-more"></span><button type="button" class="close" id="peek-close">×</button></div>
|
|
343
|
+
<div class="lightbox" id="lightbox" hidden>
|
|
344
|
+
<figure><img id="lb-img" alt=""><figcaption id="lb-cap"></figcaption></figure>
|
|
345
|
+
<button type="button" id="lb-close"></button>
|
|
346
|
+
</div>
|
|
347
|
+
</section>
|
|
348
|
+
<aside class="story">
|
|
349
|
+
<div class="text" id="story"></div>
|
|
350
|
+
<footer class="ctl">
|
|
351
|
+
<button id="prev" type="button"></button>
|
|
352
|
+
<span id="stepno"></span>
|
|
353
|
+
<div class="tabs" role="tablist"><button type="button" role="tab" id="tab-map"></button><button type="button" role="tab" id="tab-text"></button></div>
|
|
354
|
+
<button id="next" type="button"></button>
|
|
355
|
+
</footer>
|
|
356
|
+
</aside>
|
|
357
|
+
</main>
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
<!--__SCRIPTS__-->
|
|
361
|
+
<script>const ROUTE = null;</script>
|
|
362
|
+
<script src="story.js"></script>
|
|
363
|
+
<script src="../kitap.js"></script>
|
|
364
|
+
</body>
|
|
365
|
+
</html>
|