bible270 1.10.2 → 1.11.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -2
- data/README.md +13 -6
- data/app/controllers/bible270/admin_controller.rb +19 -6
- data/app/controllers/bible270/profiles_controller.rb +29 -0
- data/app/models/bible270/reader.rb +10 -1
- data/app/views/bible270/admin/show.html.erb +8 -5
- data/app/views/bible270/profiles/edit.html.erb +6 -4
- data/app/views/bible270/shared/_header.html.erb +13 -0
- data/app/views/bible270/shared/_styles.html.erb +95 -10
- data/app/views/bible270/shared/_theme_scenery.html.erb +467 -0
- data/app/views/bible270/shared/_theme_ui.html.erb +94 -0
- data/app/views/layouts/bible270/application.html.erb +2 -0
- data/config/routes.rb +1 -0
- data/lib/bible270/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31871047e1cb4638eee7d513efd350d9acbca88a02e58370c91f8506811ca5ca
|
|
4
|
+
data.tar.gz: a5fffa5b4ec2aa31addf33ff2dddd5f09ca34f2986bd8aec470cd045a87c04be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8fe16a2b159bbe1390b23ee0634e12c6bfe00e6c9e3aef711c9e8be35ac4b244b8908f4b7e11e35300d18e4211c2d02df2edb44f4d8c62834ff78bd3ee8692c1
|
|
7
|
+
data.tar.gz: c7963aa88e83095ee877211b5e192ac58da38abc880dd67323d0008b3044549cdd3a5e6ec76616046f66298030774c5dc454b864e4c6902f51f6b28cc8dd96d5
|
data/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,16 @@ All notable changes to bible270. Format follows [Keep a Changelog](https://keepa
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v1.10.
|
|
7
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v1.10.2...main)**
|
|
8
|
+
|
|
9
|
+
## [1.10.2] - 2026-10-18
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- show each Admin reader row in that reader's selected theme, with matching text and link colors
|
|
14
|
+
- let System-themed rows follow the viewing browser's light or dark preference
|
|
15
|
+
|
|
16
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v1.10.1...v1.10.2)**
|
|
8
17
|
|
|
9
18
|
## [1.10.1] - 2026-10-18
|
|
10
19
|
|
|
@@ -863,7 +872,8 @@ This is the initial public release.
|
|
|
863
872
|
|
|
864
873
|
* Email sign-in requires working Action Mailer delivery in the host application. Set `config.mailer_from`; delivery is inline by default, or set `email_sign_in_deliver_later` when a queue backend is available.
|
|
865
874
|
|
|
866
|
-
[Unreleased]: https://github.com/avonderluft/bible270/compare/v1.10.
|
|
875
|
+
[Unreleased]: https://github.com/avonderluft/bible270/compare/v1.10.2...HEAD
|
|
876
|
+
[1.10.2]: https://github.com/avonderluft/bible270/releases/tag/v1.10.2
|
|
867
877
|
[1.10.1]: https://github.com/avonderluft/bible270/releases/tag/v1.10.1
|
|
868
878
|
[1.10.0]: https://github.com/avonderluft/bible270/releases/tag/v1.10.0
|
|
869
879
|
[1.9.1]: https://github.com/avonderluft/bible270/releases/tag/v1.9.1
|
data/README.md
CHANGED
|
@@ -272,7 +272,8 @@ Admins get an extra "Admin" link in the header. The broadcast form can send an i
|
|
|
272
272
|
message either to every reader with an email address or only to readers recognized as administrators.
|
|
273
273
|
From a reader's page you can:
|
|
274
274
|
|
|
275
|
-
- **set their theme** —
|
|
275
|
+
- **set their theme** — Papyrus (Light), Meadow (Light), Ocean (Light), Cedar (Dark),
|
|
276
|
+
Forest (Dark), Moonlight (Dark), or System, independently of your own theme;
|
|
276
277
|
|
|
277
278
|
- **move them to a day** — "put them on day 42 as of today" back-dates the start date; check-offs are
|
|
278
279
|
keyed to day numbers, so nothing in their history moves;
|
|
@@ -373,15 +374,21 @@ Psalm 78: [20, 39, 55]
|
|
|
373
374
|
|
|
374
375
|
## Theme
|
|
375
376
|
|
|
376
|
-
Readers can choose **
|
|
377
|
-
**
|
|
377
|
+
Readers can choose **Papyrus (Light)**, **Meadow (Light)**, **Ocean (Light)**, **Cedar (Dark)**,
|
|
378
|
+
**Forest (Dark)**, **Moonlight (Dark)**, or **System** under **Profile → Theme**. Signed-in readers can also use
|
|
379
|
+
the compact selector beside the app title on every page; it shows the short theme names without Light/Dark suffixes.
|
|
380
|
+
Selecting a theme saves and applies it immediately without submitting other profile edits. The header and Profile
|
|
381
|
+
selectors stay synchronized, and a failed save restores the previous selection and displays an error.
|
|
382
|
+
Admin theme selections also save automatically;
|
|
383
|
+
changing another reader's theme does not change the admin's own theme. With JavaScript disabled, use Save.
|
|
384
|
+
The preference is stored
|
|
378
385
|
with their account and applies throughout Bible270 on every device. System is the default for existing
|
|
379
|
-
and new readers, follows the device's current appearance using
|
|
386
|
+
and new readers, follows the device's current appearance using Papyrus for light or Cedar for dark,
|
|
380
387
|
and responds to changes without reloading. Signed-out visitors also use System.
|
|
381
388
|
The completion dove retains its bright colors in every theme.
|
|
382
389
|
|
|
383
|
-
Existing preferences are
|
|
384
|
-
migration once display preferences are installed.
|
|
390
|
+
Existing preferences and persisted values are unchanged under the new names. Meadow, Ocean, Cedar, Forest, and Moonlight
|
|
391
|
+
require no additional migration once display preferences are installed.
|
|
385
392
|
|
|
386
393
|
Upgrading hosts must install and run migration `20260101000023`:
|
|
387
394
|
|
|
@@ -250,18 +250,21 @@ module Bible270
|
|
|
250
250
|
|
|
251
251
|
def update_theme
|
|
252
252
|
unless Reader.display_mode_column?
|
|
253
|
-
return
|
|
254
|
-
alert: 'Run the pending Bible270 database migration before changing the theme.'
|
|
253
|
+
return render_theme_error('Run the pending Bible270 database migration before changing the theme.')
|
|
255
254
|
end
|
|
256
255
|
|
|
257
256
|
mode = params[:display_mode].to_s
|
|
258
|
-
unless Reader::DISPLAY_MODES.include?(mode)
|
|
259
|
-
return redirect_to admin_reader_path(@reader), alert: 'Choose a theme from the list.'
|
|
260
|
-
end
|
|
257
|
+
return render_theme_error('Choose a theme from the list.') unless Reader::DISPLAY_MODES.include?(mode)
|
|
261
258
|
|
|
262
259
|
@reader.reload unless @reader.has_attribute?(:display_mode)
|
|
263
260
|
@reader.update!(display_mode: mode)
|
|
264
|
-
|
|
261
|
+
respond_to do |format|
|
|
262
|
+
format.json { render json: { display_mode: @reader.display_mode } }
|
|
263
|
+
format.html { redirect_to admin_reader_path(@reader), notice: 'Theme updated.' }
|
|
264
|
+
end
|
|
265
|
+
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
|
|
266
|
+
message = e.record ? e.record.errors.full_messages.to_sentence : nil
|
|
267
|
+
render_theme_error("The theme could not be saved. #{message.presence || 'Please try again.'}")
|
|
265
268
|
end
|
|
266
269
|
|
|
267
270
|
def update_notifications
|
|
@@ -355,6 +358,13 @@ module Bible270
|
|
|
355
358
|
|
|
356
359
|
private
|
|
357
360
|
|
|
361
|
+
def render_theme_error(message)
|
|
362
|
+
respond_to do |format|
|
|
363
|
+
format.json { render json: { error: message }, status: :unprocessable_entity }
|
|
364
|
+
format.html { redirect_to admin_reader_path(@reader), alert: message }
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
358
368
|
def comment_notifications_available_for?(reader)
|
|
359
369
|
return false unless Bible270.config.mention_notifications && Reader.comment_notification_columns?
|
|
360
370
|
|
|
@@ -388,6 +398,9 @@ module Bible270
|
|
|
388
398
|
def load_reader
|
|
389
399
|
@reader = Reader.find_by(id: params[:id])
|
|
390
400
|
return if @reader
|
|
401
|
+
if action_name == 'update_theme' && request.format.json?
|
|
402
|
+
return render_theme_error('Reader not found.')
|
|
403
|
+
end
|
|
391
404
|
|
|
392
405
|
redirect_to admin_path, alert: 'Reader not found.'
|
|
393
406
|
end
|
|
@@ -43,6 +43,25 @@ module Bible270
|
|
|
43
43
|
render_profile_error("Your profile could not be saved. #{message.presence || 'Please try again.'}")
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
def update_theme
|
|
47
|
+
unless Reader.display_mode_column?
|
|
48
|
+
return render_theme_error('Run the pending Bible270 database migration before changing the theme.')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
mode = params[:display_mode].to_s
|
|
52
|
+
return render_theme_error('Choose a theme from the list.') unless Reader::DISPLAY_MODES.include?(mode)
|
|
53
|
+
|
|
54
|
+
current_reader.reload unless current_reader.has_attribute?(:display_mode)
|
|
55
|
+
current_reader.update!(display_mode: mode)
|
|
56
|
+
respond_to do |format|
|
|
57
|
+
format.json { render json: { display_mode: current_reader.display_mode } }
|
|
58
|
+
format.html { redirect_to profile_path, notice: 'Theme updated.' }
|
|
59
|
+
end
|
|
60
|
+
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
|
|
61
|
+
message = e.record ? e.record.errors.full_messages.to_sentence : nil
|
|
62
|
+
render_theme_error("Your theme could not be saved. #{message.presence || 'Please try again.'}")
|
|
63
|
+
end
|
|
64
|
+
|
|
46
65
|
def remove_avatar
|
|
47
66
|
current_reader.remove_avatar!
|
|
48
67
|
redirect_to profile_path, notice: 'Your picture has been removed.'
|
|
@@ -50,6 +69,13 @@ module Bible270
|
|
|
50
69
|
|
|
51
70
|
private
|
|
52
71
|
|
|
72
|
+
def render_theme_error(message)
|
|
73
|
+
respond_to do |format|
|
|
74
|
+
format.json { render json: { error: message }, status: :unprocessable_entity }
|
|
75
|
+
format.html { redirect_to profile_path, alert: message }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
53
79
|
def display_mode_problem
|
|
54
80
|
return unless Reader.display_mode_column?
|
|
55
81
|
return if Reader::DISPLAY_MODES.include?(@display_mode)
|
|
@@ -143,6 +169,9 @@ module Bible270
|
|
|
143
169
|
# visitor who isn't signed in, so send them to sign in rather than 404.
|
|
144
170
|
def require_signed_in_reader
|
|
145
171
|
return if signed_in?
|
|
172
|
+
if action_name == 'update_theme' && request.format.json?
|
|
173
|
+
return render_theme_error('Please sign in first.')
|
|
174
|
+
end
|
|
146
175
|
|
|
147
176
|
redirect_to sign_in_path(origin: request.fullpath), alert: 'Please sign in first.'
|
|
148
177
|
end
|
|
@@ -29,7 +29,16 @@ module Bible270
|
|
|
29
29
|
|
|
30
30
|
PASSAGE_SOURCES = %w[bible_com bible_gateway blue_letter_bible].freeze
|
|
31
31
|
DEFAULT_PASSAGE_SOURCE = 'bible_com'
|
|
32
|
-
|
|
32
|
+
DISPLAY_OPTIONS = {
|
|
33
|
+
'Papyrus (Light)' => 'light',
|
|
34
|
+
'Meadow (Light)' => 'sage',
|
|
35
|
+
'Ocean (Light)' => 'mist',
|
|
36
|
+
'Cedar (Dark)' => 'dark',
|
|
37
|
+
'Forest (Dark)' => 'dark_green',
|
|
38
|
+
'Moonlight (Dark)' => 'dark_blue',
|
|
39
|
+
'System' => 'system'
|
|
40
|
+
}.freeze
|
|
41
|
+
DISPLAY_MODES = DISPLAY_OPTIONS.values.freeze
|
|
33
42
|
DEFAULT_DISPLAY_MODE = 'system'
|
|
34
43
|
DAILY_REMINDER_TIME_FORMAT = %r{\A(?:[01]\d|2[0-3]):[0-5]\d\z}
|
|
35
44
|
DAILY_REMINDER_MINUTES = %w[00 15 30 45].freeze
|
|
@@ -82,13 +82,15 @@
|
|
|
82
82
|
<% if Bible270::Reader.display_mode_column? %>
|
|
83
83
|
<%= form_with url: admin_reader_theme_path(@reader), method: :patch, class: "b270-emailform" do %>
|
|
84
84
|
<%= select_tag :display_mode,
|
|
85
|
-
options_for_select(
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
options_for_select(Bible270::Reader::DISPLAY_OPTIONS, @reader.effective_display_mode),
|
|
86
|
+
class: "b270-select", aria: { labelledby: "b270-theme-heading", describedby: "b270-theme-hint b270-theme-status" },
|
|
87
|
+
data: { b270_theme_url: admin_reader_theme_path(@reader), b270_theme_apply: @reader == current_reader,
|
|
88
|
+
b270_saved_theme: @reader.effective_display_mode } %>
|
|
88
89
|
<p id="b270-theme-hint" class="b270-hint" style="margin:0">
|
|
89
|
-
System follows the reader's device setting:
|
|
90
|
+
System follows the reader's device setting: Papyrus for light, Cedar for dark.
|
|
90
91
|
</p>
|
|
91
|
-
|
|
92
|
+
<p id="b270-theme-status" class="b270-hint" role="status" aria-live="polite" style="margin:0"></p>
|
|
93
|
+
<%= submit_tag "Save", class: "b270-btn", data: { b270_theme_fallback: true } %>
|
|
92
94
|
<% end %>
|
|
93
95
|
<% else %>
|
|
94
96
|
<p class="b270-flash alert" style="margin:0">
|
|
@@ -97,6 +99,7 @@
|
|
|
97
99
|
<% end %>
|
|
98
100
|
</div>
|
|
99
101
|
|
|
102
|
+
|
|
100
103
|
<div class="b270-panel">
|
|
101
104
|
<h2 class="b270-subhead">Email notifications</h2>
|
|
102
105
|
<% unless Bible270.config.mention_notifications %>
|
|
@@ -48,11 +48,13 @@
|
|
|
48
48
|
<label class="b270-field">
|
|
49
49
|
<span>Theme</span>
|
|
50
50
|
<%= select_tag :display_mode,
|
|
51
|
-
options_for_select(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
options_for_select(Bible270::Reader::DISPLAY_OPTIONS, @display_mode),
|
|
52
|
+
class: "b270-select", aria: { describedby: "b270-display-hint b270-theme-status" },
|
|
53
|
+
data: { b270_theme_url: profile_theme_path, b270_theme_apply: true,
|
|
54
|
+
b270_saved_theme: current_reader.effective_display_mode } %>
|
|
55
|
+
<span id="b270-display-hint" class="b270-hint" style="margin:0">System follows your device's setting: Papyrus for light, Cedar for dark.</span>
|
|
55
56
|
</label>
|
|
57
|
+
<p id="b270-theme-status" class="b270-hint" role="status" aria-live="polite" style="margin:0"></p>
|
|
56
58
|
<% else %>
|
|
57
59
|
<p class="b270-flash alert" style="margin:0">
|
|
58
60
|
Theme settings are temporarily unavailable while this installation finishes a database update.
|
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
<%= b270_mark %>
|
|
5
5
|
<span class="b270-title"><%= Bible270.config.app_name %></span>
|
|
6
6
|
<% end %>
|
|
7
|
+
<% if current_reader && Bible270::Reader.display_mode_column? %>
|
|
8
|
+
<%= form_with url: profile_theme_path, method: :patch, class: "b270-header-theme" do %>
|
|
9
|
+
<%= select_tag :display_mode,
|
|
10
|
+
options_for_select(Bible270::Reader::DISPLAY_OPTIONS.map { |label, value| [label.sub(/ \((?:Light|Dark)\)\z/, ""), value] }, current_reader.effective_display_mode),
|
|
11
|
+
id: "b270-header-theme", class: "b270-header-theme-select",
|
|
12
|
+
aria: { label: "Theme", describedby: "b270-header-theme-status" },
|
|
13
|
+
data: { b270_theme_url: profile_theme_path, b270_theme_apply: true,
|
|
14
|
+
b270_saved_theme: current_reader.effective_display_mode,
|
|
15
|
+
b270_theme_status: "b270-header-theme-status" } %>
|
|
16
|
+
<%= submit_tag "Save", class: "b270-btn", data: { b270_theme_fallback: true } %>
|
|
17
|
+
<span id="b270-header-theme-status" class="b270-header-theme-status" role="status" aria-live="polite"></span>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
7
20
|
</div>
|
|
8
21
|
<nav class="b270-nav" aria-label="Primary navigation">
|
|
9
22
|
<div class="b270-desktopnav">
|
|
@@ -22,6 +22,36 @@
|
|
|
22
22
|
Only system mode follows the media query, including live OS changes. */
|
|
23
23
|
.b270-reader-theme[data-display-mode="light"],
|
|
24
24
|
.b270[data-display-mode="light"]{color-scheme:light}
|
|
25
|
+
.b270-reader-theme[data-display-mode="sage"],
|
|
26
|
+
.b270[data-display-mode="sage"]{
|
|
27
|
+
color-scheme:light;
|
|
28
|
+
background-image:none;
|
|
29
|
+
--paper:#e1e8df; --card:#edf0e9; --ink:#29382d; --muted:#52634f; --faint:#5b6c58;
|
|
30
|
+
--line:#cbd6c7; --line-soft:#dce4d8; --teal:#356b60; --oxblood:#905466; --gold:#776023;
|
|
31
|
+
--gold-lite:#776023; --shadow:0 1px 0 rgba(41,56,45,.04),0 10px 30px -18px rgba(41,56,45,.25);
|
|
32
|
+
--control:#fcfdf9; --control-line:#768572; --task-surface:rgba(255,255,255,.35); --button-hover:#3b5040;
|
|
33
|
+
--on-accent:#ffffff; --complete-ink:#ffffff; --complete-dot:#edf0e9;
|
|
34
|
+
--button-surface:#29382d; --button-ink:#edf0e9;
|
|
35
|
+
--teal-fill:#356b60; --oxblood-fill:#905466; --gold-fill:#776023;
|
|
36
|
+
--teal-mark:#356b60; --oxblood-mark:#905466; --gold-mark:#776023;
|
|
37
|
+
--alert-surface:#f5e7e9; --alert-ink:#783f4c; --alert-line:#d9b7c0;
|
|
38
|
+
--notice-surface:#e3eee0; --notice-ink:#375b37; --notice-line:#b8d1b1;
|
|
39
|
+
}
|
|
40
|
+
.b270-reader-theme[data-display-mode="mist"],
|
|
41
|
+
.b270[data-display-mode="mist"]{
|
|
42
|
+
color-scheme:light;
|
|
43
|
+
background-image:none;
|
|
44
|
+
--paper:#e2e7ed; --card:#ecf0f5; --ink:#293746; --muted:#4f6074; --faint:#58687b;
|
|
45
|
+
--line:#ced7e2; --line-soft:#dfe5ed; --teal:#316977; --oxblood:#86566f; --gold:#7b602b;
|
|
46
|
+
--gold-lite:#7b602b; --shadow:0 1px 0 rgba(41,55,70,.04),0 10px 30px -18px rgba(41,55,70,.25);
|
|
47
|
+
--control:#fcfdff; --control-line:#758496; --task-surface:rgba(255,255,255,.35); --button-hover:#3c5065;
|
|
48
|
+
--on-accent:#ffffff; --complete-ink:#ffffff; --complete-dot:#ecf0f5;
|
|
49
|
+
--button-surface:#293746; --button-ink:#ecf0f5;
|
|
50
|
+
--teal-fill:#316977; --oxblood-fill:#86566f; --gold-fill:#7b602b;
|
|
51
|
+
--teal-mark:#316977; --oxblood-mark:#86566f; --gold-mark:#7b602b;
|
|
52
|
+
--alert-surface:#f3e7ee; --alert-ink:#754357; --alert-line:#d7bbc9;
|
|
53
|
+
--notice-surface:#e3eeea; --notice-ink:#355c50; --notice-line:#b7d0c7;
|
|
54
|
+
}
|
|
25
55
|
.b270-reader-theme[data-display-mode="dark"],
|
|
26
56
|
.b270[data-display-mode="dark"]{
|
|
27
57
|
color-scheme:dark;
|
|
@@ -88,7 +118,48 @@
|
|
|
88
118
|
--notice-surface:#25382a; --notice-ink:#b6dfb6; --notice-line:#6d956e;
|
|
89
119
|
}
|
|
90
120
|
}
|
|
121
|
+
/* A fixed SVG avoids mobile background-attachment quirks and stays behind all content. */
|
|
122
|
+
.b270-theme-scenery{display:none;--b270-scene-height:clamp(280px,52vh,560px);position:fixed;inset:auto 0 0;width:100%;height:var(--b270-scene-height);z-index:-1;pointer-events:none}
|
|
123
|
+
.b270{isolation:isolate}
|
|
124
|
+
.b270 > .b270-theme-scripture{display:block;color:var(--gold)}
|
|
125
|
+
.b270[data-display-mode="dark_blue"] > .b270-theme-scripture{color:#c6d6e9}
|
|
126
|
+
.b270[data-display-mode="mist"] > .b270-theme-scenery[data-scenery="mist"],
|
|
127
|
+
.b270[data-display-mode="mist"] > .b270-theme-scenery[data-scenery="mist_sky"],
|
|
128
|
+
.b270[data-display-mode="mist"] > .b270-theme-scenery[data-scenery="ocean_gulls"],
|
|
129
|
+
.b270[data-display-mode="sage"] > .b270-theme-scenery[data-scenery="sage"],
|
|
130
|
+
.b270[data-display-mode="sage"] > .b270-theme-scenery[data-scenery="sage_sky"],
|
|
131
|
+
.b270[data-display-mode="dark"] > .b270-theme-scenery[data-scenery="dark"],
|
|
132
|
+
.b270[data-display-mode="dark_green"] > .b270-theme-scenery[data-scenery="dark_green"],
|
|
133
|
+
.b270[data-display-mode="dark_blue"] > .b270-theme-scenery[data-scenery="dark_blue"],
|
|
134
|
+
.b270[data-display-mode="dark_blue"] > .b270-theme-scenery[data-scenery="hebrew_water"],
|
|
135
|
+
.b270[data-display-mode="light"] > .b270-theme-scenery[data-scenery="light"],
|
|
136
|
+
.b270[data-display-mode="system"] > .b270-theme-scenery[data-scenery="light"]{display:block}
|
|
137
|
+
.b270-theme-scenery[data-scenery="light"],.b270-theme-scripture{font-size:clamp(12px,2.2vw,30px)}
|
|
138
|
+
.b270-theme-scripture{height:clamp(380px,76vh,820px)}
|
|
139
|
+
.b270-theme-scenery[data-scenery="light"]{height:100%;overflow:hidden}
|
|
140
|
+
.b270-papyrus-grid{transform-box:view-box;transform:translateY(calc(100% - clamp(380px,76vh,820px) * .7))}
|
|
141
|
+
.b270-papyrus-opposite{transform-box:view-box;transform-origin:0 0;transform:translate(100%,100%) rotate(180deg)}
|
|
142
|
+
@media (max-width:600px){
|
|
143
|
+
.b270-papyrus-corner{transform:scale(.55)}
|
|
144
|
+
}
|
|
145
|
+
.b270-theme-scenery[data-scenery="dark"]{height:100%}
|
|
146
|
+
.b270-theme-scenery[data-scenery="sage_sky"]{height:100%}
|
|
147
|
+
.b270-theme-scenery[data-scenery="mist_sky"]{height:100%}
|
|
148
|
+
.b270-ocean-gulls{transform-box:view-box;transform:translate(calc(100% - 115px),calc(100% - 35px))}
|
|
149
|
+
/* Match the moon's 90-unit right inset in its 1600 × 440, right-aligned slice viewport. */
|
|
150
|
+
.b270-scripture-letters [lang="el"]{transform-box:view-box;transform:translateX(calc(100% - max(5.625%, var(--b270-scene-height) * 90 / 440)))}
|
|
151
|
+
.b270-scripture-letters [lang="el"] tspan{font-size:.9em}
|
|
152
|
+
/* Scale reflected Hebrew with the lake artwork, allowing for its smaller letterforms. */
|
|
153
|
+
.b270-hebrew-water{font-size:max(2vw, var(--b270-scene-height) * 32 / 440);transform-box:view-box;transform:translate(10%,calc(100% - max(12.5vw, var(--b270-scene-height) * 200 / 440)))}
|
|
154
|
+
@media (prefers-color-scheme:dark){
|
|
155
|
+
.b270[data-display-mode="system"] > .b270-theme-scenery[data-scenery="light"]{display:none}
|
|
156
|
+
.b270[data-display-mode="system"] > .b270-theme-scenery[data-scenery="dark"]{display:block}
|
|
157
|
+
}
|
|
158
|
+
@media print{
|
|
159
|
+
.b270[data-display-mode] > .b270-theme-scenery[data-scenery]{display:none}
|
|
160
|
+
}
|
|
91
161
|
.b270 *{box-sizing:border-box}
|
|
162
|
+
.b270 [data-b270-theme-fallback][hidden]{display:none}
|
|
92
163
|
/* Blanket anchor colour. NB: this is (0,1,1), so any single-class rule
|
|
93
164
|
colouring an <a> (.b270-btn, .b270-cell, .b270-arrow ...) must be
|
|
94
165
|
qualified with .b270 to outrank it, or the link inherits body colour. */
|
|
@@ -96,14 +167,26 @@
|
|
|
96
167
|
.b270 :where(a,button,input,select,textarea,summary):focus-visible{outline:2px solid var(--gold);outline-offset:2px}
|
|
97
168
|
.b270-wrap{max-width:820px;margin:0 auto;padding:clamp(14px,3.5vw,36px) clamp(14px,4vw,40px) 64px}
|
|
98
169
|
|
|
99
|
-
.b270-topbar{display:flex;align-items:center;gap:
|
|
100
|
-
.b270-brand{display:flex;margin-right:auto}
|
|
170
|
+
.b270-topbar{display:flex;align-items:center;gap:12px;flex-wrap:nowrap;padding-bottom:20px;border-bottom:1px solid var(--line-soft);margin-bottom:26px}
|
|
171
|
+
.b270-brand{display:flex;align-items:baseline;gap:4px;margin-right:auto;min-width:0}
|
|
172
|
+
.b270-header-theme{position:relative;display:flex;align-items:baseline;gap:4px;margin:0;flex:none}
|
|
173
|
+
.b270-header-theme-select{font:inherit;font-size:12px;width:6.5em;height:28px;padding:2px;margin:0;
|
|
174
|
+
border:1px solid var(--line);border-radius:6px;background:var(--card);color:var(--ink);cursor:pointer}
|
|
175
|
+
@supports (field-sizing:content){
|
|
176
|
+
.b270-header-theme-select{field-sizing:content;width:auto}
|
|
177
|
+
}
|
|
178
|
+
.b270-header-theme-select:disabled{cursor:wait;opacity:.7}
|
|
179
|
+
.b270-header-theme-status:not([data-error="true"]){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
|
|
180
|
+
clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
181
|
+
.b270-header-theme-status[data-error="true"]{position:absolute;z-index:30;right:0;top:calc(100% + 6px);width:220px;max-width:calc(100vw - 28px);
|
|
182
|
+
padding:8px 10px;border:1px solid var(--alert-line);border-radius:8px;background:var(--alert-surface);
|
|
183
|
+
color:var(--alert-ink);font-size:12px;line-height:1.5;box-shadow:var(--shadow)}
|
|
101
184
|
/* The whole brand is one link, so the mark and the words highlight together. */
|
|
102
|
-
.b270-brandlink{display:flex;align-items:
|
|
103
|
-
.b270-mark{display:flex;align-items:center;flex:none;line-height:0}
|
|
104
|
-
.b270-mark svg,.b270-mark img{display:block;border-radius:8px}
|
|
105
|
-
.b270-title{font-family:"Spectral",Georgia,serif;font-weight:600;font-size:
|
|
106
|
-
.b270-nav{display:flex;align-items:center;font-size:14px}
|
|
185
|
+
.b270-brandlink{display:flex;align-items:baseline;gap:6px;text-decoration:none;min-width:0}
|
|
186
|
+
.b270-mark{display:flex;align-items:center;align-self:center;flex:none;line-height:0}
|
|
187
|
+
.b270-mark svg,.b270-mark img,.b270-brandlink > img.b270-mark{display:block;width:28px;height:28px;border-radius:8px}
|
|
188
|
+
.b270-title{font-family:"Spectral",Georgia,serif;font-weight:600;font-size:18px;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
189
|
+
.b270-nav{display:flex;align-items:center;font-size:14px;flex:none;white-space:nowrap}
|
|
107
190
|
.b270-desktopnav{display:flex;align-items:center;gap:16px}
|
|
108
191
|
.b270-navmenu{display:none}
|
|
109
192
|
.b270-nav a{color:var(--muted);text-decoration:none}
|
|
@@ -475,9 +558,10 @@
|
|
|
475
558
|
.b270-footer p{margin:0;line-height:1.5}
|
|
476
559
|
details.b270-index summary{cursor:pointer;font-family:"Spectral",serif;font-size:18px;font-weight:600;list-style:none;margin-top:30px}
|
|
477
560
|
details.b270-index summary::-webkit-details-marker{display:none}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
.b270-
|
|
561
|
+
/* Switch to the compact menu before the full navigation can crowd the theme control. */
|
|
562
|
+
@media (max-width:820px){
|
|
563
|
+
.b270-topbar{flex-wrap:nowrap;align-items:center;gap:8px}
|
|
564
|
+
|
|
481
565
|
.b270-nav{position:relative;flex:none}
|
|
482
566
|
.b270-desktopnav{display:none}
|
|
483
567
|
.b270-navmenu{display:block}
|
|
@@ -497,6 +581,7 @@
|
|
|
497
581
|
.b270-navlinks a:hover,.b270-nav .b270-linkbtn:hover{background:var(--paper)}
|
|
498
582
|
.b270-nav form{display:block;width:100%}
|
|
499
583
|
}
|
|
584
|
+
|
|
500
585
|
@media (max-width:480px){
|
|
501
586
|
.b270-tm{grid-template-columns:104px 1fr auto}
|
|
502
587
|
.b270-avatarrow{align-items:flex-start;flex-wrap:wrap}
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
<%# Kept in the DOM so changing themes can reveal the artwork without a page reload. %>
|
|
2
|
+
<%# Keep the saved "mist" preference compatible while presenting it as Ocean. %>
|
|
3
|
+
<svg class="b270-theme-scenery" data-scenery="mist_sky" viewBox="0 0 1600 1000" preserveAspectRatio="none"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
5
|
+
<defs>
|
|
6
|
+
<linearGradient id="b270-ocean-sky" x1="0" y1="0" x2="0" y2="1">
|
|
7
|
+
<stop offset="0" stop-color="#b9d2e5" stop-opacity=".7" />
|
|
8
|
+
<stop offset=".5" stop-color="#dce6e9" stop-opacity=".45" />
|
|
9
|
+
<stop offset=".75" stop-color="#eee5d3" stop-opacity=".25" />
|
|
10
|
+
<stop offset="1" stop-color="#e2e7ed" stop-opacity="0" />
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<radialGradient id="b270-ocean-afternoon-glow">
|
|
13
|
+
<stop offset="0" stop-color="#fff1ce" stop-opacity=".4" />
|
|
14
|
+
<stop offset="1" stop-color="#fff1ce" stop-opacity="0" />
|
|
15
|
+
</radialGradient>
|
|
16
|
+
</defs>
|
|
17
|
+
<rect width="1600" height="1000" fill="url(#b270-ocean-sky)" />
|
|
18
|
+
<ellipse cx="1420" cy="180" rx="460" ry="340" fill="url(#b270-ocean-afternoon-glow)" />
|
|
19
|
+
<g id="b270-ocean-clouds" fill="#fffdf7" opacity=".25">
|
|
20
|
+
<path d="M45 148Q154 127 242 130T409 115Q343 143 245 144T45 148Z" />
|
|
21
|
+
<path d="M948 87Q1050 69 1141 73T1322 57Q1237 84 1144 86T948 87Z" />
|
|
22
|
+
</g>
|
|
23
|
+
</svg>
|
|
24
|
+
|
|
25
|
+
<svg class="b270-theme-scenery" data-scenery="mist" viewBox="0 0 1600 440" preserveAspectRatio="xMidYMax slice"
|
|
26
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
27
|
+
<defs>
|
|
28
|
+
<linearGradient id="b270-ocean-water" x1="0" y1="0" x2="0" y2="1">
|
|
29
|
+
<stop offset="0" stop-color="#91b2c6" stop-opacity=".65" />
|
|
30
|
+
<stop offset=".55" stop-color="#a3c6ce" stop-opacity=".75" />
|
|
31
|
+
<stop offset="1" stop-color="#b9d4cf" stop-opacity=".7" />
|
|
32
|
+
</linearGradient>
|
|
33
|
+
<linearGradient id="b270-ocean-sand" x1="0" y1="0" x2="0" y2="1">
|
|
34
|
+
<stop offset="0" stop-color="#c8c9b6" stop-opacity=".8" />
|
|
35
|
+
<stop offset=".35" stop-color="#d9cfb9" stop-opacity=".85" />
|
|
36
|
+
<stop offset="1" stop-color="#e6ddcb" stop-opacity=".9" />
|
|
37
|
+
</linearGradient>
|
|
38
|
+
<linearGradient id="b270-ocean-shimmer" x1="0" y1="0" x2="1" y2="0">
|
|
39
|
+
<stop offset="0" stop-color="#fff6de" stop-opacity="0" />
|
|
40
|
+
<stop offset=".65" stop-color="#fff6de" stop-opacity=".35" />
|
|
41
|
+
<stop offset="1" stop-color="#fff6de" stop-opacity=".1" />
|
|
42
|
+
</linearGradient>
|
|
43
|
+
</defs>
|
|
44
|
+
<rect x="0" y="135" width="1600" height="305" fill="url(#b270-ocean-water)" />
|
|
45
|
+
<path id="b270-ocean-horizon" d="M0 135H1600" fill="none" stroke="#7f9eae" stroke-width="1" opacity=".3" />
|
|
46
|
+
<g id="b270-ocean-distant-waves" fill="none" stroke="#f5f5eb" stroke-linecap="round" opacity=".35">
|
|
47
|
+
<path d="M22 154H178M235 158H442M532 151H708M811 159H1032M1119 153H1385M1460 160H1590" stroke-width="1" />
|
|
48
|
+
<path d="M0 181Q150 174 294 181T601 184M728 182Q874 175 1028 181T1370 180M1442 183H1600" stroke-width="1.4" />
|
|
49
|
+
<path d="M43 216Q193 203 353 214T691 213M821 220Q970 208 1128 218T1510 215" stroke-width="2" />
|
|
50
|
+
</g>
|
|
51
|
+
<g fill="none" stroke="url(#b270-ocean-shimmer)" stroke-linecap="round">
|
|
52
|
+
<path d="M880 170H1360M808 196H1434M751 230H1507" stroke-width="3" />
|
|
53
|
+
<path d="M685 269Q1050 253 1560 272" stroke-width="5" />
|
|
54
|
+
</g>
|
|
55
|
+
<path id="b270-ocean-beach" fill="url(#b270-ocean-sand)"
|
|
56
|
+
d="M0 365C230 338 396 388 617 361S981 304 1190 322 1450 346 1600 310V440H0Z" />
|
|
57
|
+
<path id="b270-ocean-wash" fill="#f9f8ed" opacity=".24"
|
|
58
|
+
d="M0 343C230 318 396 367 617 340S981 283 1190 301 1450 325 1600 289V310C1450 346 1399 340 1190 322S838 334 617 361 230 338 0 365Z" />
|
|
59
|
+
<g id="b270-ocean-surf" fill="none" stroke="#fffdf2" stroke-linecap="round">
|
|
60
|
+
<path d="M0 343C230 318 396 367 617 340S981 283 1190 301 1450 325 1600 289" stroke-width="2.8" opacity=".6" />
|
|
61
|
+
<path d="M0 292C208 270 381 310 593 289S973 254 1181 266 1444 288 1600 257" stroke-width="2" opacity=".38" />
|
|
62
|
+
<path d="M50 365Q174 352 282 366M370 381Q499 389 611 374M745 352Q887 326 1012 328M1194 339Q1335 359 1450 344" stroke-width="1.4" opacity=".38" />
|
|
63
|
+
</g>
|
|
64
|
+
<g fill="none" stroke="#b4a88c" stroke-linecap="round" opacity=".17">
|
|
65
|
+
<path d="M105 414Q228 401 347 413M530 424Q669 412 777 417M1007 386Q1110 379 1227 391M1337 420Q1450 406 1562 411" />
|
|
66
|
+
</g>
|
|
67
|
+
</svg>
|
|
68
|
+
|
|
69
|
+
<svg class="b270-theme-scenery" data-scenery="ocean_gulls"
|
|
70
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
71
|
+
<%# A viewport-based layer keeps both birds on the lower-right sand when the coast is cropped on phones. %>
|
|
72
|
+
<defs>
|
|
73
|
+
<g id="b270-ocean-gull">
|
|
74
|
+
<ellipse cx="0" cy="12" rx="17" ry="2.5" fill="#968e78" opacity=".2" />
|
|
75
|
+
<path d="M-3 3-4 11-8 12M4 3 6 11 10 12" fill="none" stroke="#a18a60" stroke-width="1.2" stroke-linecap="round" />
|
|
76
|
+
<path d="M-19-5-11-4Q-6-11 4-8L8-15Q11-19 15-15Q18-11 13-8L11-1Q7 7-5 4Q-12 2-19-5Z" fill="#f7f5eb" />
|
|
77
|
+
<path d="M-17-5Q-5-11 5-6Q2 1-9 0Z" fill="#8898a0" />
|
|
78
|
+
<path d="M-17-5-10-3-7 0-12-1Z" fill="#52656c" />
|
|
79
|
+
<path d="M15-14 22-11 15-10Z" fill="#b49a63" />
|
|
80
|
+
<circle cx="13" cy="-14" r=".8" fill="#53636a" />
|
|
81
|
+
</g>
|
|
82
|
+
</defs>
|
|
83
|
+
<g class="b270-ocean-gulls" opacity=".65">
|
|
84
|
+
<use href="#b270-ocean-gull" transform="translate(0 -8) scale(.95 .95)" />
|
|
85
|
+
<use href="#b270-ocean-gull" transform="translate(60 3) scale(-1.15 1.15)" />
|
|
86
|
+
</g>
|
|
87
|
+
</svg>
|
|
88
|
+
|
|
89
|
+
<svg class="b270-theme-scenery" data-scenery="sage_sky" viewBox="0 0 1600 1000" preserveAspectRatio="none"
|
|
90
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
91
|
+
<defs>
|
|
92
|
+
<linearGradient id="b270-meadow-blue-sky" x1="0" y1="0" x2="0" y2="1">
|
|
93
|
+
<stop offset="0" stop-color="#bdd8eb" stop-opacity=".8" />
|
|
94
|
+
<stop offset=".4" stop-color="#d1e2ea" stop-opacity=".55" />
|
|
95
|
+
<stop offset=".75" stop-color="#e1e8df" stop-opacity="0" />
|
|
96
|
+
</linearGradient>
|
|
97
|
+
<linearGradient id="b270-meadow-cloud-fade" x1="0" y1="0" x2="1" y2="0">
|
|
98
|
+
<stop offset="0" stop-color="#fffdf5" stop-opacity="0" />
|
|
99
|
+
<stop offset=".35" stop-color="#fffdf5" stop-opacity=".5" />
|
|
100
|
+
<stop offset=".7" stop-color="#fffdf5" stop-opacity=".35" />
|
|
101
|
+
<stop offset="1" stop-color="#fffdf5" stop-opacity="0" />
|
|
102
|
+
</linearGradient>
|
|
103
|
+
</defs>
|
|
104
|
+
<rect width="1600" height="1000" fill="url(#b270-meadow-blue-sky)" />
|
|
105
|
+
<g id="b270-meadow-clouds" fill="url(#b270-meadow-cloud-fade)">
|
|
106
|
+
<path d="M20 105Q105 99 162 84T275 81Q322 82 376 72Q317 94 253 94T142 106Q78 111 20 105Z" />
|
|
107
|
+
<path d="M368 51Q448 45 497 34T586 36Q635 37 690 26Q628 46 562 47T458 52Q407 55 368 51Z" />
|
|
108
|
+
<path d="M1002 99Q1098 90 1160 74T1265 73Q1329 76 1400 61Q1328 86 1247 87T1130 99Q1065 105 1002 99Z" />
|
|
109
|
+
<path d="M1270 161Q1352 153 1405 139T1502 140Q1553 141 1610 128Q1546 151 1481 152T1371 163Q1315 166 1270 161Z" />
|
|
110
|
+
</g>
|
|
111
|
+
</svg>
|
|
112
|
+
|
|
113
|
+
<svg class="b270-theme-scenery" data-scenery="sage" viewBox="0 0 1600 440" preserveAspectRatio="xMaxYMax slice"
|
|
114
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
115
|
+
<defs>
|
|
116
|
+
<clipPath id="b270-meadow-sky">
|
|
117
|
+
<path d="M0 280C160 180 360 180 560 250S900 260 1040 210 1400 200 1600 270V0H0Z" />
|
|
118
|
+
</clipPath>
|
|
119
|
+
<radialGradient id="b270-meadow-glow">
|
|
120
|
+
<stop offset="0" stop-color="#f8e8b6" stop-opacity=".55" />
|
|
121
|
+
<stop offset=".5" stop-color="#f6e8c4" stop-opacity=".25" />
|
|
122
|
+
<stop offset="1" stop-color="#f6e8c4" stop-opacity="0" />
|
|
123
|
+
</radialGradient>
|
|
124
|
+
<linearGradient id="b270-meadow-rays" x1="0" y1="0" x2="0" y2="1">
|
|
125
|
+
<stop offset="0" stop-color="#f8e8b6" stop-opacity="0" />
|
|
126
|
+
<stop offset=".45" stop-color="#f8e8b6" stop-opacity=".22" />
|
|
127
|
+
<stop offset="1" stop-color="#f8e8b6" stop-opacity=".4" />
|
|
128
|
+
</linearGradient>
|
|
129
|
+
<linearGradient id="b270-meadow-distant" x1="0" y1="0" x2="0" y2="1">
|
|
130
|
+
<stop offset="0" stop-color="#c2cfb7" />
|
|
131
|
+
<stop offset="1" stop-color="#e1e8df" />
|
|
132
|
+
</linearGradient>
|
|
133
|
+
<linearGradient id="b270-meadow-middle" x1="0" y1="0" x2="0" y2="1">
|
|
134
|
+
<stop offset="0" stop-color="#afc1a3" />
|
|
135
|
+
<stop offset="1" stop-color="#e1e8df" />
|
|
136
|
+
</linearGradient>
|
|
137
|
+
<linearGradient id="b270-meadow-near" x1="0" y1="0" x2="0" y2="1">
|
|
138
|
+
<stop offset="0" stop-color="#9fb693" />
|
|
139
|
+
<stop offset="1" stop-color="#e1e8df" />
|
|
140
|
+
</linearGradient>
|
|
141
|
+
</defs>
|
|
142
|
+
<g clip-path="url(#b270-meadow-sky)">
|
|
143
|
+
<ellipse cx="1560" cy="245" rx="440" ry="200" fill="url(#b270-meadow-glow)" />
|
|
144
|
+
<path fill="url(#b270-meadow-rays)"
|
|
145
|
+
d="M1560 260 860 0 1020 0Z M1560 260 1180 0 1295 0Z M1560 260 1435 0 1515 0Z M1560 260 1605 0 1685 0Z M1560 260 1825 0 1940 0Z M1560 260 2100 0 2260 0Z" />
|
|
146
|
+
<circle cx="1560" cy="260" r="61" fill="#f6e5b0" opacity=".55" />
|
|
147
|
+
</g>
|
|
148
|
+
<path fill="url(#b270-meadow-distant)" opacity=".65"
|
|
149
|
+
d="M0 280C160 180 360 180 560 250S900 260 1040 210 1400 200 1600 270V440H0Z" />
|
|
150
|
+
<path fill="url(#b270-meadow-middle)" opacity=".55"
|
|
151
|
+
d="M0 320C240 260 350 260 550 300S850 340 1090 290 1420 260 1600 320V440H0Z" />
|
|
152
|
+
<path fill="url(#b270-meadow-near)" opacity=".45"
|
|
153
|
+
d="M0 370C200 340 430 350 640 375S1030 340 1220 360 1480 400 1600 370V440H0Z" />
|
|
154
|
+
<g id="b270-meadow-grass-distant" fill="none" stroke="#7e9672" stroke-width="1" stroke-linecap="round" opacity=".16">
|
|
155
|
+
<path d="M232 287Q232 275 226 269M232 287Q236 277 242 274" />
|
|
156
|
+
<path d="M405 301Q408 286 402 280M405 301Q399 292 393 290" />
|
|
157
|
+
<path d="M708 317Q706 305 711 299M708 317Q714 309 720 307" />
|
|
158
|
+
<path d="M1015 302Q1015 287 1009 281M1015 302Q1020 291 1026 289" />
|
|
159
|
+
<path d="M1278 287Q1280 273 1274 266M1278 287Q1284 278 1290 275" />
|
|
160
|
+
<path d="M1540 320Q1538 306 1544 299M1540 320Q1534 310 1528 308" />
|
|
161
|
+
</g>
|
|
162
|
+
<g id="b270-meadow-grass-near" fill="none" stroke="#7e9672" stroke-width="1.6" stroke-linecap="round" opacity=".24">
|
|
163
|
+
<path d="M110 409Q112 380 99 364M110 409Q119 387 130 380M110 409Q101 393 91 389" />
|
|
164
|
+
<path d="M153 415Q150 394 159 378M153 415Q145 402 136 398" />
|
|
165
|
+
<path d="M1434 418Q1433 392 1446 375M1434 418Q1424 399 1412 393" />
|
|
166
|
+
<path d="M1480 411Q1482 381 1470 366M1480 411Q1491 387 1505 380M1480 411Q1471 397 1463 393" />
|
|
167
|
+
<path d="M285 388Q287 367 279 356M285 388Q293 372 301 368M285 388Q278 378 271 375" />
|
|
168
|
+
<path d="M451 420Q448 398 457 385M451 420Q442 407 435 404" />
|
|
169
|
+
<path d="M635 408Q637 385 629 374M635 408Q644 391 653 387" />
|
|
170
|
+
<path d="M850 383Q849 363 855 354M850 383Q842 369 833 366" />
|
|
171
|
+
<path d="M1064 409Q1066 384 1057 371M1064 409Q1074 390 1084 386M1064 409Q1056 397 1047 393" />
|
|
172
|
+
<path d="M1227 425Q1224 403 1232 391M1227 425Q1218 410 1209 407" />
|
|
173
|
+
<path d="M1570 404Q1572 383 1564 371M1570 404Q1578 389 1587 385" />
|
|
174
|
+
</g>
|
|
175
|
+
</svg>
|
|
176
|
+
|
|
177
|
+
<svg class="b270-theme-scenery" data-scenery="light"
|
|
178
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
179
|
+
<defs>
|
|
180
|
+
<%# CSS anchors the full-page grid to the unchanged scripture baseline. %>
|
|
181
|
+
<pattern id="b270-papyrus-fibres" width="4em" height="3.6em"
|
|
182
|
+
viewBox="0 0 120 108" patternUnits="userSpaceOnUse">
|
|
183
|
+
<g fill="none" stroke="#a48b60" stroke-width=".85" opacity=".23">
|
|
184
|
+
<path d="M0 12H120M0 48H120M0 84H120" />
|
|
185
|
+
<path d="M18 0Q16 28 19 60T18 108M55 0Q58 35 54 72T55 108M96 0Q94 30 97 67T96 108" />
|
|
186
|
+
</g>
|
|
187
|
+
<path d="M8 25H43M78 52H111M32 76H67" stroke="#fff9ed" stroke-width="1" opacity=".3" />
|
|
188
|
+
</pattern>
|
|
189
|
+
<pattern id="b270-papyrus-edge-top" width="240" height="12" patternUnits="userSpaceOnUse">
|
|
190
|
+
<path d="M0 0H240V5L228 7 218 4 203 6 191 3 175 7 162 5 149 8 132 5 118 6 103 3 91 7 77 5 61 8 49 4 35 6 22 3 10 7 0 5Z" fill="#c9bda4" />
|
|
191
|
+
<path d="M0 5L10 7 22 3 35 6 49 4 61 8 77 5 91 7 103 3 118 6 132 5 149 8 162 5 175 7 191 3 203 6 218 4 228 7 240 5" fill="none" stroke="#a48b60" stroke-opacity=".32" />
|
|
192
|
+
</pattern>
|
|
193
|
+
<pattern id="b270-papyrus-edge-side" width="12" height="240" patternUnits="userSpaceOnUse">
|
|
194
|
+
<path d="M0 0H5L7 14 4 28 6 43 3 59 7 74 5 89 8 105 4 121 6 137 3 152 7 168 5 186 8 203 4 221 5 240H0Z" fill="#c9bda4" />
|
|
195
|
+
<path d="M5 0L7 14 4 28 6 43 3 59 7 74 5 89 8 105 4 121 6 137 3 152 7 168 5 186 8 203 4 221 5 240" fill="none" stroke="#a48b60" stroke-opacity=".32" />
|
|
196
|
+
</pattern>
|
|
197
|
+
<linearGradient id="b270-papyrus-curl" x1="0" y1="0" x2="1" y2="1">
|
|
198
|
+
<stop offset="0" stop-color="#bbaa89" />
|
|
199
|
+
<stop offset=".45" stop-color="#dfd3b9" />
|
|
200
|
+
<stop offset=".78" stop-color="#fff7e5" />
|
|
201
|
+
<stop offset="1" stop-color="#c5b38f" />
|
|
202
|
+
</linearGradient>
|
|
203
|
+
<radialGradient id="b270-papyrus-curl-shadow">
|
|
204
|
+
<stop offset="0" stop-color="#705b37" stop-opacity=".25" />
|
|
205
|
+
<stop offset="1" stop-color="#705b37" stop-opacity="0" />
|
|
206
|
+
</radialGradient>
|
|
207
|
+
<g id="b270-papyrus-corner">
|
|
208
|
+
<ellipse cx="29" cy="31" rx="43" ry="31" fill="url(#b270-papyrus-curl-shadow)" transform="rotate(-42 29 31)" />
|
|
209
|
+
<path d="M0 0H64Q31 14 0 64Z" fill="#c9bda4" />
|
|
210
|
+
<path d="M0 64Q15 27 64 0Q48 24 54 49Q29 43 0 64Z" fill="url(#b270-papyrus-curl)" stroke="#a48b60" stroke-opacity=".3" stroke-width=".7" />
|
|
211
|
+
<path d="M3 61Q26 43 54 49" fill="none" stroke="#fff9ed" stroke-opacity=".65" />
|
|
212
|
+
</g>
|
|
213
|
+
</defs>
|
|
214
|
+
<g class="b270-papyrus-grid">
|
|
215
|
+
<rect y="-100%" width="100%" height="200%" fill="url(#b270-papyrus-fibres)" />
|
|
216
|
+
</g>
|
|
217
|
+
<g class="b270-papyrus-edges">
|
|
218
|
+
<rect width="100%" height="12" fill="url(#b270-papyrus-edge-top)" />
|
|
219
|
+
<rect width="12" height="100%" fill="url(#b270-papyrus-edge-side)" />
|
|
220
|
+
</g>
|
|
221
|
+
<g class="b270-papyrus-edges b270-papyrus-opposite">
|
|
222
|
+
<rect width="100%" height="12" fill="url(#b270-papyrus-edge-top)" />
|
|
223
|
+
<rect width="12" height="100%" fill="url(#b270-papyrus-edge-side)" />
|
|
224
|
+
</g>
|
|
225
|
+
<use class="b270-papyrus-corner" href="#b270-papyrus-corner" />
|
|
226
|
+
<g class="b270-papyrus-opposite">
|
|
227
|
+
<use class="b270-papyrus-corner" href="#b270-papyrus-corner" />
|
|
228
|
+
</g>
|
|
229
|
+
</svg>
|
|
230
|
+
|
|
231
|
+
<svg class="b270-theme-scenery" data-scenery="dark"
|
|
232
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
233
|
+
<defs>
|
|
234
|
+
<pattern id="b270-cedar-grain" width="600" height="900" patternUnits="userSpaceOnUse">
|
|
235
|
+
<g fill="#20190f" opacity=".32">
|
|
236
|
+
<path d="M44 0 38 92 49 166 35 246 43 325 30 412 40 492 29 578 42 659 34 744 48 825 44 900H57L61 824 48 743 57 658 43 578 53 492 44 413 56 326 48 248 62 166 51 92 57 0Z" />
|
|
237
|
+
<path d="M204 0 215 116 201 210 210 294 195 378 207 469 194 559 205 648 193 735 208 819 204 900H217L220 818 207 735 218 648 207 559 220 468 209 378 223 295 215 210 227 116 217 0Z" />
|
|
238
|
+
<path d="M372 0 359 104 371 193 356 283 369 369 358 460 372 547 359 635 373 723 362 815 372 900H385L375 814 387 723 373 635 386 547 372 459 383 369 370 282 385 193 373 105 385 0Z" />
|
|
239
|
+
<path d="M529 0 538 92 523 184 536 276 522 362 533 451 518 542 531 636 520 726 534 813 529 900H540L547 813 533 726 544 636 532 542 546 452 535 362 549 276 537 184 551 92 540 0Z" />
|
|
240
|
+
</g>
|
|
241
|
+
<g fill="none" stroke="#ad8359" stroke-linecap="round" opacity=".17">
|
|
242
|
+
<path stroke-width="2" d="M75 0C51 134 85 209 65 330S79 525 62 653 82 811 75 900M241 0C260 112 231 202 250 314S229 511 247 629 225 802 241 900M402 0C383 127 413 223 393 345S418 534 398 665 414 810 402 900M565 0C581 123 552 223 574 348S552 548 571 677 552 807 565 900" />
|
|
243
|
+
<path d="M97 51 88 147 99 232M95 468 84 556 94 631 85 710M272 79 264 164 276 245 267 320M272 502 261 588 273 667 264 757 272 830M455 43 446 132 458 219 449 295M471 408 459 486 471 568M472 767 461 842" />
|
|
244
|
+
<path d="M163 47C150 163 167 221 150 271M150 416C165 481 151 539 160 617M316 171 307 262 318 347 308 429M311 671 302 753 313 845M491 109 481 195 492 281 482 359" />
|
|
245
|
+
</g>
|
|
246
|
+
<g fill="none" stroke="#9b744e" stroke-width="1.5" opacity=".2">
|
|
247
|
+
<path d="M143 261C113 290 108 342 126 385L143 425C158 390 175 346 162 302Z" />
|
|
248
|
+
<path d="M142 288C126 310 124 340 135 365L144 387C152 359 158 329 149 307Z" />
|
|
249
|
+
<path d="M141 315Q134 335 144 352Q150 332 141 315Z" />
|
|
250
|
+
<path d="M433 576C410 608 405 650 424 695L434 731C449 694 463 655 452 614Z" />
|
|
251
|
+
<path d="M433 604C421 627 420 650 430 677L435 695C443 671 448 646 440 624Z" />
|
|
252
|
+
</g>
|
|
253
|
+
</pattern>
|
|
254
|
+
<linearGradient id="b270-cedar-fade" x1="0" y1="0" x2="0" y2="1">
|
|
255
|
+
<stop offset="0" stop-color="white" stop-opacity="0" />
|
|
256
|
+
<stop offset=".28" stop-color="white" stop-opacity=".5" />
|
|
257
|
+
<stop offset=".7" stop-color="white" stop-opacity=".9" />
|
|
258
|
+
<stop offset="1" stop-color="white" />
|
|
259
|
+
</linearGradient>
|
|
260
|
+
<mask id="b270-cedar-mask">
|
|
261
|
+
<rect width="100%" height="100%" fill="url(#b270-cedar-fade)" />
|
|
262
|
+
</mask>
|
|
263
|
+
</defs>
|
|
264
|
+
<rect width="100%" height="100%" fill="url(#b270-cedar-grain)" mask="url(#b270-cedar-mask)" />
|
|
265
|
+
</svg>
|
|
266
|
+
|
|
267
|
+
<svg class="b270-theme-scenery" data-scenery="dark_green" viewBox="0 0 1600 440" preserveAspectRatio="xMidYMax slice"
|
|
268
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
269
|
+
<defs>
|
|
270
|
+
<path id="b270-spruce-tree"
|
|
271
|
+
d="M0 0-9 29-5 27-20 55-11 52-29 85-18 81-39 119-26 113-48 154-32 146-57 194-6 181-6 218H6V181L57 194 32 146 48 154 26 113 39 119 18 81 29 85 11 52 20 55 5 27 9 29Z" />
|
|
272
|
+
<linearGradient id="b270-spruce-fade" x1="0" y1="0" x2="0" y2="1">
|
|
273
|
+
<stop offset="0" stop-color="white" />
|
|
274
|
+
<stop offset=".75" stop-color="white" stop-opacity=".9" />
|
|
275
|
+
<stop offset="1" stop-color="white" stop-opacity="0" />
|
|
276
|
+
</linearGradient>
|
|
277
|
+
<mask id="b270-spruce-mask" maskUnits="userSpaceOnUse" x="0" y="0" width="1600" height="440">
|
|
278
|
+
<rect width="1600" height="440" fill="url(#b270-spruce-fade)" />
|
|
279
|
+
</mask>
|
|
280
|
+
</defs>
|
|
281
|
+
<g id="b270-spruce-treeline" transform="translate(0 -24)">
|
|
282
|
+
<g id="b270-spruce-distant" fill="#66816b" opacity=".19" mask="url(#b270-spruce-mask)">
|
|
283
|
+
<rect id="b270-spruce-distant-ground" y="300" width="1600" height="164" />
|
|
284
|
+
<use href="#b270-spruce-tree" transform="translate(15 145) scale(.64 .95)" />
|
|
285
|
+
<use href="#b270-spruce-tree" transform="translate(90 171) scale(.55 .82)" />
|
|
286
|
+
<use href="#b270-spruce-tree" transform="translate(164 112) scale(.7 1.1)" />
|
|
287
|
+
<use href="#b270-spruce-tree" transform="translate(244 157) scale(.6 .92)" />
|
|
288
|
+
<use href="#b270-spruce-tree" transform="translate(321 137) scale(.66 1.02)" />
|
|
289
|
+
<use href="#b270-spruce-tree" transform="translate(405 186) scale(.55 .82)" />
|
|
290
|
+
<use href="#b270-spruce-tree" transform="translate(478 163) scale(.67 .96)" />
|
|
291
|
+
<use href="#b270-spruce-tree" transform="translate(560 194) scale(.58 .83)" />
|
|
292
|
+
<use href="#b270-spruce-tree" transform="translate(643 180) scale(.6 .91)" />
|
|
293
|
+
<use href="#b270-spruce-tree" transform="translate(719 212) scale(.5 .77)" />
|
|
294
|
+
<use href="#b270-spruce-tree" transform="translate(790 192) scale(.58 .88)" />
|
|
295
|
+
<use href="#b270-spruce-tree" transform="translate(875 211) scale(.52 .78)" />
|
|
296
|
+
<use href="#b270-spruce-tree" transform="translate(950 174) scale(.62 .96)" />
|
|
297
|
+
<use href="#b270-spruce-tree" transform="translate(1030 193) scale(.54 .84)" />
|
|
298
|
+
<use href="#b270-spruce-tree" transform="translate(1110 148) scale(.7 1.02)" />
|
|
299
|
+
<use href="#b270-spruce-tree" transform="translate(1195 166) scale(.58 .91)" />
|
|
300
|
+
<use href="#b270-spruce-tree" transform="translate(1270 108) scale(.7 1.15)" />
|
|
301
|
+
<use href="#b270-spruce-tree" transform="translate(1350 153) scale(.6 .94)" />
|
|
302
|
+
<use href="#b270-spruce-tree" transform="translate(1435 129) scale(.64 1.05)" />
|
|
303
|
+
<use href="#b270-spruce-tree" transform="translate(1515 168) scale(.58 .88)" />
|
|
304
|
+
<use href="#b270-spruce-tree" transform="translate(1590 139) scale(.7 1)" />
|
|
305
|
+
<use href="#b270-spruce-tree" transform="translate(53 237) scale(.3 .36)" />
|
|
306
|
+
<use href="#b270-spruce-tree" transform="translate(205 225) scale(.34 .43)" />
|
|
307
|
+
<use href="#b270-spruce-tree" transform="translate(365 247) scale(.28 .31)" />
|
|
308
|
+
<use href="#b270-spruce-tree" transform="translate(520 231) scale(.33 .4)" />
|
|
309
|
+
<use href="#b270-spruce-tree" transform="translate(681 241) scale(.29 .34)" />
|
|
310
|
+
<use href="#b270-spruce-tree" transform="translate(834 218) scale(.36 .47)" />
|
|
311
|
+
<use href="#b270-spruce-tree" transform="translate(1068 237) scale(.31 .37)" />
|
|
312
|
+
<use href="#b270-spruce-tree" transform="translate(1235 244) scale(.28 .33)" />
|
|
313
|
+
<use href="#b270-spruce-tree" transform="translate(1394 228) scale(.34 .42)" />
|
|
314
|
+
</g>
|
|
315
|
+
<%# Keep the foreground outside the distant haze mask so no trees show through it. %>
|
|
316
|
+
<g id="b270-spruce-near" fill="#0c2017">
|
|
317
|
+
<rect id="b270-spruce-near-ground" y="350" width="1600" height="114" />
|
|
318
|
+
<use href="#b270-spruce-tree" transform="translate(-15 104) scale(1.1 1.65)" />
|
|
319
|
+
<use href="#b270-spruce-tree" transform="translate(115 64) scale(1.08 1.78)" />
|
|
320
|
+
<use href="#b270-spruce-tree" transform="translate(228 153) scale(.87 1.4)" />
|
|
321
|
+
<use href="#b270-spruce-tree" transform="translate(343 121) scale(1 1.56)" />
|
|
322
|
+
<use href="#b270-spruce-tree" transform="translate(455 189) scale(.83 1.26)" />
|
|
323
|
+
<use href="#b270-spruce-tree" transform="translate(561 161) scale(.94 1.4)" />
|
|
324
|
+
<use href="#b270-spruce-tree" transform="translate(676 227) scale(.75 1.12)" />
|
|
325
|
+
<use href="#b270-spruce-tree" transform="translate(781 202) scale(.88 1.25)" />
|
|
326
|
+
<use href="#b270-spruce-tree" transform="translate(890 231) scale(.76 1.1)" />
|
|
327
|
+
<use href="#b270-spruce-tree" transform="translate(998 182) scale(.89 1.32)" />
|
|
328
|
+
<use href="#b270-spruce-tree" transform="translate(1112 153) scale(.98 1.46)" />
|
|
329
|
+
<use href="#b270-spruce-tree" transform="translate(1227 106) scale(1.03 1.65)" />
|
|
330
|
+
<use href="#b270-spruce-tree" transform="translate(1343 162) scale(.9 1.38)" />
|
|
331
|
+
<use href="#b270-spruce-tree" transform="translate(1460 73) scale(1.12 1.78)" />
|
|
332
|
+
<use href="#b270-spruce-tree" transform="translate(1580 133) scale(.97 1.5)" />
|
|
333
|
+
<use href="#b270-spruce-tree" transform="translate(55 213) scale(.62 1.08)" />
|
|
334
|
+
<use href="#b270-spruce-tree" transform="translate(394 245) scale(.58 .94)" />
|
|
335
|
+
<use href="#b270-spruce-tree" transform="translate(622 132) scale(.78 1.52)" />
|
|
336
|
+
<use href="#b270-spruce-tree" transform="translate(944 263) scale(.55 .88)" />
|
|
337
|
+
<use href="#b270-spruce-tree" transform="translate(1171 230) scale(.65 1.04)" />
|
|
338
|
+
<use href="#b270-spruce-tree" transform="translate(1524 201) scale(.7 1.17)" />
|
|
339
|
+
<use href="#b270-spruce-tree" transform="translate(173 282) scale(.34 .4)" />
|
|
340
|
+
<use href="#b270-spruce-tree" transform="translate(283 265) scale(.4 .49)" />
|
|
341
|
+
<use href="#b270-spruce-tree" transform="translate(506 290) scale(.32 .35)" />
|
|
342
|
+
<use href="#b270-spruce-tree" transform="translate(727 271) scale(.38 .46)" />
|
|
343
|
+
<use href="#b270-spruce-tree" transform="translate(835 297) scale(.3 .31)" />
|
|
344
|
+
<use href="#b270-spruce-tree" transform="translate(1054 277) scale(.36 .43)" />
|
|
345
|
+
<use href="#b270-spruce-tree" transform="translate(1284 288) scale(.33 .36)" />
|
|
346
|
+
<use href="#b270-spruce-tree" transform="translate(1398 260) scale(.42 .52)" />
|
|
347
|
+
<use href="#b270-spruce-tree" transform="translate(1552 294) scale(.31 .33)" />
|
|
348
|
+
</g>
|
|
349
|
+
</g>
|
|
350
|
+
</svg>
|
|
351
|
+
|
|
352
|
+
<svg class="b270-theme-scenery" data-scenery="dark_blue" viewBox="0 0 1600 440" preserveAspectRatio="xMaxYMax slice"
|
|
353
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
354
|
+
<defs>
|
|
355
|
+
<radialGradient id="b270-moon-halo">
|
|
356
|
+
<stop offset="0" stop-color="#c7d8ec" stop-opacity=".16" />
|
|
357
|
+
<stop offset=".45" stop-color="#9fb7d3" stop-opacity=".07" />
|
|
358
|
+
<stop offset="1" stop-color="#9fb7d3" stop-opacity="0" />
|
|
359
|
+
</radialGradient>
|
|
360
|
+
<linearGradient id="b270-lake-water" x1="0" y1="0" x2="0" y2="1">
|
|
361
|
+
<stop offset="0" stop-color="#526e91" stop-opacity=".14" />
|
|
362
|
+
<stop offset="1" stop-color="#526e91" stop-opacity="0" />
|
|
363
|
+
</linearGradient>
|
|
364
|
+
<linearGradient id="b270-moon-reflection" x1="0" y1="0" x2="0" y2="1">
|
|
365
|
+
<stop offset="0" stop-color="#c6d6e9" stop-opacity=".3" />
|
|
366
|
+
<stop offset=".5" stop-color="#abc3df" stop-opacity=".17" />
|
|
367
|
+
<stop offset="1" stop-color="#abc3df" stop-opacity="0" />
|
|
368
|
+
</linearGradient>
|
|
369
|
+
<filter id="b270-water-far" x="-20%" y="-50%" width="140%" height="200%" color-interpolation-filters="sRGB">
|
|
370
|
+
<feTurbulence type="fractalNoise" baseFrequency=".008 .14" numOctaves="1" seed="7" result="ripples" />
|
|
371
|
+
<feDisplacementMap in="SourceGraphic" in2="ripples" scale="2" xChannelSelector="R" yChannelSelector="G" />
|
|
372
|
+
<feGaussianBlur stdDeviation=".15" />
|
|
373
|
+
</filter>
|
|
374
|
+
<filter id="b270-water-middle" x="-20%" y="-50%" width="140%" height="200%" color-interpolation-filters="sRGB">
|
|
375
|
+
<feTurbulence type="fractalNoise" baseFrequency=".008 .14" numOctaves="1" seed="7" result="ripples" />
|
|
376
|
+
<feDisplacementMap in="SourceGraphic" in2="ripples" scale="5" xChannelSelector="R" yChannelSelector="G" />
|
|
377
|
+
<feGaussianBlur stdDeviation=".45" />
|
|
378
|
+
</filter>
|
|
379
|
+
<filter id="b270-water-near" x="-20%" y="-50%" width="140%" height="200%" color-interpolation-filters="sRGB">
|
|
380
|
+
<feTurbulence type="fractalNoise" baseFrequency=".008 .14" numOctaves="1" seed="7" result="ripples" />
|
|
381
|
+
<feDisplacementMap in="SourceGraphic" in2="ripples" scale="9" xChannelSelector="R" yChannelSelector="G" />
|
|
382
|
+
<feGaussianBlur stdDeviation="1.1" />
|
|
383
|
+
</filter>
|
|
384
|
+
<mask id="b270-greek-reflection" maskUnits="userSpaceOnUse" x="0" y="240" width="1600" height="200">
|
|
385
|
+
<g fill="white" font-family="Georgia, Times New Roman, serif" font-size="28" text-anchor="middle">
|
|
386
|
+
<g filter="url(#b270-water-far)">
|
|
387
|
+
<text transform="translate(1510 250) scale(.38 -.22)">ΟΛΟΓΟΣ</text>
|
|
388
|
+
<text transform="translate(1510 270) scale(.5 -.28)">ΘΕΟΣΗΝ</text>
|
|
389
|
+
</g>
|
|
390
|
+
<g filter="url(#b270-water-middle)">
|
|
391
|
+
<text transform="translate(1510 293) scale(.66 -.36)">ΘΕΟΝΚΑΙ</text>
|
|
392
|
+
<text transform="translate(1510 319) scale(.8 -.44)">ΗΝΠΡΟΣΤΟΝ</text>
|
|
393
|
+
<text transform="translate(1510 348) scale(.95 -.54)">ΚΑΙΟΛΟΓΟΣ</text>
|
|
394
|
+
</g>
|
|
395
|
+
<g filter="url(#b270-water-near)">
|
|
396
|
+
<text transform="translate(1510 380) scale(1.1 -.68)">ΗΝΟΛΟΓΟΣ</text>
|
|
397
|
+
<text transform="translate(1510 411) scale(1.4 -.85)">ΕΝΑΡΧΗ</text>
|
|
398
|
+
</g>
|
|
399
|
+
</g>
|
|
400
|
+
</mask>
|
|
401
|
+
<clipPath id="b270-lake-clip">
|
|
402
|
+
<rect y="240" width="1600" height="200" />
|
|
403
|
+
</clipPath>
|
|
404
|
+
</defs>
|
|
405
|
+
<circle cx="1510" cy="162" r="105" fill="url(#b270-moon-halo)" />
|
|
406
|
+
<circle id="b270-midnight-moon" cx="1510" cy="162" r="31" fill="#c7d5e5" opacity=".55" />
|
|
407
|
+
<g fill="#71869d" opacity=".1">
|
|
408
|
+
<ellipse cx="1501" cy="155" rx="9" ry="12" />
|
|
409
|
+
<ellipse cx="1518" cy="173" rx="11" ry="7" />
|
|
410
|
+
</g>
|
|
411
|
+
<path id="b270-lake-shore" fill="#0c1829" opacity=".42"
|
|
412
|
+
d="M0 224Q110 201 230 216T465 220 710 226 950 215 1190 222 1420 224 1600 218V240H0Z" />
|
|
413
|
+
<rect y="240" width="1600" height="200" fill="url(#b270-lake-water)" />
|
|
414
|
+
<path d="M0 240H1600" stroke="#8fa7c3" stroke-width=".7" opacity=".12" />
|
|
415
|
+
<g clip-path="url(#b270-lake-clip)">
|
|
416
|
+
<ellipse cx="1510" cy="305" rx="145" ry="125" fill="url(#b270-moon-halo)" opacity=".6" />
|
|
417
|
+
<rect id="b270-lake-reflection" fill="url(#b270-moon-reflection)"
|
|
418
|
+
x="0" y="240" width="1600" height="200" mask="url(#b270-greek-reflection)" />
|
|
419
|
+
<%# The rippled letters supply the water texture without straight lines crossing the reflections. %>
|
|
420
|
+
</g>
|
|
421
|
+
</svg>
|
|
422
|
+
|
|
423
|
+
<svg class="b270-theme-scenery" data-scenery="hebrew_water"
|
|
424
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
425
|
+
<%# A viewport-based layer keeps the faint reflection beneath the Hebrew column on narrow screens. %>
|
|
426
|
+
<g class="b270-hebrew-water" lang="he" direction="rtl" unicode-bidi="isolate"
|
|
427
|
+
fill="#c6d6e9" text-anchor="middle" opacity=".4" font-family="Noto Serif Hebrew, Times New Roman, serif">
|
|
428
|
+
<g filter="url(#b270-water-far)" opacity=".3">
|
|
429
|
+
<text transform="translate(0 10) scale(.38 -.22)">הארץ</text>
|
|
430
|
+
<text transform="translate(0 30) scale(.5 -.28)">ואת</text>
|
|
431
|
+
</g>
|
|
432
|
+
<g filter="url(#b270-water-middle)" opacity=".2">
|
|
433
|
+
<text transform="translate(0 53) scale(.66 -.36)">השּׁמים</text>
|
|
434
|
+
<text transform="translate(0 79) scale(.8 -.44)">את</text>
|
|
435
|
+
<text transform="translate(0 108) scale(.95 -.54)">אלהים</text>
|
|
436
|
+
</g>
|
|
437
|
+
<g filter="url(#b270-water-near)" opacity=".09">
|
|
438
|
+
<text transform="translate(0 140) scale(1.1 -.68)">בּרא</text>
|
|
439
|
+
<text transform="translate(0 171) scale(1.4 -.85)">בּראשׁית</text>
|
|
440
|
+
</g>
|
|
441
|
+
</g>
|
|
442
|
+
</svg>
|
|
443
|
+
|
|
444
|
+
<svg class="b270-theme-scenery b270-theme-scripture" data-scenery="scripture"
|
|
445
|
+
xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
446
|
+
<%# Shared across themes; explicit lines preserve word breaks and Hebrew vowel marks. %>
|
|
447
|
+
<g class="b270-scripture-letters" fill="currentColor" text-anchor="middle" opacity=".22">
|
|
448
|
+
<g lang="he" direction="rtl" unicode-bidi="isolate" font-family="Noto Serif Hebrew, Times New Roman, serif">
|
|
449
|
+
<text x="10%" y="30%" dy="-3.2em">בּראשׁית</text>
|
|
450
|
+
<text x="10%" y="30%" dy="-2em">בּרא</text>
|
|
451
|
+
<text x="10%" y="30%" dy="-.8em">אלהים</text>
|
|
452
|
+
<text x="10%" y="30%" dy=".4em">את</text>
|
|
453
|
+
<text x="10%" y="30%" dy="1.6em">השּׁמים</text>
|
|
454
|
+
<text x="10%" y="30%" dy="2.8em">ואת</text>
|
|
455
|
+
<text x="10%" y="30%" dy="4em">הארץ</text>
|
|
456
|
+
</g>
|
|
457
|
+
<g lang="el" direction="ltr" text-anchor="middle" font-family="Georgia, Times New Roman, serif">
|
|
458
|
+
<text y="30%" dy="-3.2em"><tspan>ΕΝΑΡΧΗ</tspan></text>
|
|
459
|
+
<text y="30%" dy="-2em"><tspan>ΗΝΟΛΟΓΟΣ</tspan></text>
|
|
460
|
+
<text y="30%" dy="-.8em"><tspan>ΚΑΙΟΛΟΓΟΣ</tspan></text>
|
|
461
|
+
<text y="30%" dy=".4em"><tspan>ΗΝΠΡΟΣΤΟΝ</tspan></text>
|
|
462
|
+
<text y="30%" dy="1.6em"><tspan>ΘΕΟΝΚΑΙ</tspan></text>
|
|
463
|
+
<text y="30%" dy="2.8em"><tspan>ΘΕΟΣΗΝ</tspan></text>
|
|
464
|
+
<text y="30%" dy="4em"><tspan>ΟΛΟΓΟΣ</tspan></text>
|
|
465
|
+
</g>
|
|
466
|
+
</g>
|
|
467
|
+
</svg>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script nonce="<%= content_security_policy_nonce %>" data-refresh-path="<%= refresh_session_path %>">
|
|
2
|
+
(() => {
|
|
3
|
+
if (window.Bible270ThemeUI) {
|
|
4
|
+
window.Bible270ThemeUI.initialize();
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const refreshPath = document.currentScript.dataset.refreshPath;
|
|
9
|
+
const selector = "select[data-b270-theme-url]";
|
|
10
|
+
|
|
11
|
+
function status(select, message) {
|
|
12
|
+
const region = document.getElementById(select.dataset.b270ThemeStatus || "b270-theme-status");
|
|
13
|
+
if (region) {
|
|
14
|
+
region.textContent = message;
|
|
15
|
+
region.dataset.error = String(message.startsWith("Couldn’t save"));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function initialize() {
|
|
20
|
+
document.querySelectorAll("[data-b270-theme-fallback]").forEach((button) => { button.hidden = true; });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
document.addEventListener("change", async (event) => {
|
|
24
|
+
const select = event.target;
|
|
25
|
+
if (!select.matches(selector) || select.disabled) return;
|
|
26
|
+
|
|
27
|
+
const mode = select.value;
|
|
28
|
+
const group = Array.from(document.querySelectorAll(selector))
|
|
29
|
+
.filter((other) => other.isConnected && (
|
|
30
|
+
other.dataset.b270ThemeUrl === select.dataset.b270ThemeUrl ||
|
|
31
|
+
(other.dataset.b270ThemeApply === "true" && select.dataset.b270ThemeApply === "true")
|
|
32
|
+
))
|
|
33
|
+
.map((other) => ({ select: other, previous: other.dataset.b270SavedTheme }));
|
|
34
|
+
group.forEach(({ select: other }) => {
|
|
35
|
+
other.disabled = true;
|
|
36
|
+
other.setAttribute("aria-busy", "true");
|
|
37
|
+
if (other !== select) {
|
|
38
|
+
const region = document.getElementById(other.dataset.b270ThemeStatus || "b270-theme-status");
|
|
39
|
+
if (region?.dataset.error === "true") status(other, "");
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
status(select, "Saving theme…");
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// A suspended mobile tab may need a fresh session and CSRF token.
|
|
46
|
+
const session = await fetch(refreshPath, {
|
|
47
|
+
credentials: "same-origin", cache: "no-store", headers: { Accept: "text/plain" }
|
|
48
|
+
});
|
|
49
|
+
const token = session.headers.get("X-CSRF-Token");
|
|
50
|
+
if (!session.ok || !token) throw new Error("Could not refresh the session.");
|
|
51
|
+
const meta = document.querySelector("meta[name='csrf-token']");
|
|
52
|
+
if (meta) meta.content = token;
|
|
53
|
+
document.querySelectorAll("input[name='authenticity_token']").forEach((input) => { input.value = token; });
|
|
54
|
+
|
|
55
|
+
const response = await fetch(select.dataset.b270ThemeUrl, {
|
|
56
|
+
method: "PATCH", credentials: "same-origin", cache: "no-store",
|
|
57
|
+
headers: { Accept: "application/json", "Content-Type": "application/json", "X-CSRF-Token": token },
|
|
58
|
+
body: JSON.stringify({ display_mode: mode })
|
|
59
|
+
});
|
|
60
|
+
const result = await response.json();
|
|
61
|
+
if (!response.ok || result.display_mode !== mode) throw new Error("Theme was not saved.");
|
|
62
|
+
|
|
63
|
+
group.forEach(({ select: other }) => {
|
|
64
|
+
other.value = mode;
|
|
65
|
+
other.dataset.b270SavedTheme = mode;
|
|
66
|
+
});
|
|
67
|
+
if (!select.isConnected) return;
|
|
68
|
+
if (select.dataset.b270ThemeApply === "true") document.body.dataset.displayMode = mode;
|
|
69
|
+
status(select, "Theme saved.");
|
|
70
|
+
} catch (_error) {
|
|
71
|
+
group.forEach(({ select: other, previous }) => { other.value = previous; });
|
|
72
|
+
if (select.isConnected) status(select, "Couldn’t save the theme. Please select it again to retry.");
|
|
73
|
+
} finally {
|
|
74
|
+
group.forEach(({ select: other }) => {
|
|
75
|
+
other.disabled = false;
|
|
76
|
+
other.removeAttribute("aria-busy");
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Do not race a profile submission against an in-flight theme update.
|
|
82
|
+
document.addEventListener("submit", (event) => {
|
|
83
|
+
const select = event.target.querySelector(selector);
|
|
84
|
+
if (select?.disabled) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
status(select, "Please wait for the theme to finish saving.");
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
document.addEventListener("turbo:load", initialize);
|
|
91
|
+
window.Bible270ThemeUI = { initialize };
|
|
92
|
+
initialize();
|
|
93
|
+
})();
|
|
94
|
+
</script>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<%= yield :head %>
|
|
15
15
|
</head>
|
|
16
16
|
<body class="b270" data-display-mode="<%= current_reader&.effective_display_mode || Bible270::Reader::DEFAULT_DISPLAY_MODE %>">
|
|
17
|
+
<%= render "bible270/shared/theme_scenery" %>
|
|
17
18
|
<div class="b270-wrap">
|
|
18
19
|
<%= render "bible270/shared/header" %>
|
|
19
20
|
<% if flash[:alert] %><div class="b270-flash alert"><%= flash[:alert] %></div><% end %>
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
<%= render 'bible270/shared/day_index', expanded: content_for?(:expand_day_index) unless content_for?(:hide_day_index) %>
|
|
24
25
|
<%= b270_footer %>
|
|
25
26
|
</div>
|
|
27
|
+
<%= render "bible270/shared/theme_ui" %>
|
|
26
28
|
<%= render "bible270/shared/scripture_popups" %>
|
|
27
29
|
<%= render "bible270/shared/video_embeds" %>
|
|
28
30
|
</body>
|
data/config/routes.rb
CHANGED
|
@@ -44,6 +44,7 @@ Bible270::Engine.routes.draw do
|
|
|
44
44
|
|
|
45
45
|
get 'profile', to: 'profiles#edit', as: :profile
|
|
46
46
|
patch 'profile', to: 'profiles#update'
|
|
47
|
+
patch 'profile/theme', to: 'profiles#update_theme', as: :profile_theme
|
|
47
48
|
delete 'profile/avatar', to: 'profiles#remove_avatar', as: :profile_avatar
|
|
48
49
|
|
|
49
50
|
get 'reflections', to: 'comments#index', as: :reflections
|
data/lib/bible270/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bible270
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew vonderLuft
|
|
@@ -189,6 +189,8 @@ files:
|
|
|
189
189
|
- app/views/bible270/shared/_reflections.html.erb
|
|
190
190
|
- app/views/bible270/shared/_scripture_popups.html.erb
|
|
191
191
|
- app/views/bible270/shared/_styles.html.erb
|
|
192
|
+
- app/views/bible270/shared/_theme_scenery.html.erb
|
|
193
|
+
- app/views/bible270/shared/_theme_ui.html.erb
|
|
192
194
|
- app/views/bible270/shared/_track_bars.html.erb
|
|
193
195
|
- app/views/bible270/shared/_video_embeds.html.erb
|
|
194
196
|
- app/views/bible270/sign_in_mailer/magic_link.html.erb
|