panda-core 0.10.7 → 0.12.2
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/README.md +35 -0
- data/app/assets/tailwind/application.css +5 -0
- data/app/models/concerns/panda/core/has_uuid.rb +24 -0
- data/app/models/panda/core/user.rb +22 -19
- data/config/brakeman.ignore +51 -0
- data/db/migrate/20250809000001_create_panda_core_users.rb +14 -9
- data/db/migrate/20251203100000_rename_is_admin_to_admin_in_panda_core_users.rb +18 -0
- data/lib/panda/core/configuration.rb +4 -0
- data/lib/panda/core/engine/autoload_config.rb +9 -10
- data/lib/panda/core/engine/omniauth_config.rb +82 -36
- data/lib/panda/core/engine/route_config.rb +37 -0
- data/lib/panda/core/engine.rb +46 -41
- data/lib/panda/core/middleware.rb +146 -0
- data/lib/panda/core/shared/inflections_config.rb +1 -0
- data/lib/panda/core/testing/rails_helper.rb +17 -8
- data/lib/panda/core/testing/support/authentication_helpers.rb +6 -6
- data/lib/panda/core/testing/support/authentication_test_helpers.rb +2 -12
- data/lib/panda/core/testing/support/system/browser_console_logger.rb +1 -2
- data/lib/panda/core/testing/support/system/chrome_path.rb +38 -0
- data/lib/panda/core/testing/support/system/cuprite_helpers.rb +79 -0
- data/lib/panda/core/testing/support/system/cuprite_setup.rb +61 -66
- data/lib/panda/core/testing/support/system/system_test_helpers.rb +11 -11
- data/lib/panda/core/version.rb +1 -1
- data/lib/tasks/panda/core/users.rake +3 -3
- data/lib/tasks/panda/shared.rake +31 -5
- data/public/panda-core-assets/favicons/browserconfig.xml +1 -1
- data/public/panda-core-assets/favicons/site.webmanifest +1 -1
- data/public/panda-core-assets/panda-core-0.11.0.css +2 -0
- data/public/panda-core-assets/panda-core.css +1 -1
- metadata +9 -9
- data/db/migrate/20250810120000_add_current_theme_to_panda_core_users.rb +0 -7
- data/db/migrate/20250811120000_add_oauth_avatar_url_to_panda_core_users.rb +0 -7
- data/lib/panda/core/engine/middleware_config.rb +0 -17
- data/lib/panda/core/testing/support/system/better_system_tests.rb +0 -180
- data/lib/panda/core/testing/support/system/capybara_config.rb +0 -64
- data/lib/panda/core/testing/support/system/ci_capybara_config.rb +0 -77
- data/public/panda-core-assets/panda-core-0.10.6.css +0 -2
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Extends Rails system tests with improved screenshot and driver handling
|
|
4
|
-
#
|
|
5
|
-
# This module provides enhancements to Rails system tests:
|
|
6
|
-
# - Multi-session screenshot support
|
|
7
|
-
# - CI-specific error handling and logging
|
|
8
|
-
# - Enhanced failure screenshots with HTML debugging
|
|
9
|
-
# - Network idle waiting before screenshots
|
|
10
|
-
#
|
|
11
|
-
# @example Using in RSpec
|
|
12
|
-
# RSpec.configure do |config|
|
|
13
|
-
# config.include Panda::Core::Testing::BetterSystemTests, type: :system
|
|
14
|
-
# end
|
|
15
|
-
module Panda
|
|
16
|
-
module Core
|
|
17
|
-
module Testing
|
|
18
|
-
module BetterSystemTests
|
|
19
|
-
# Make failure screenshots compatible with multi-session setup.
|
|
20
|
-
# That's where we use Capybara.last_used_session introduced before.
|
|
21
|
-
def take_screenshot
|
|
22
|
-
return super unless Capybara.last_used_session
|
|
23
|
-
|
|
24
|
-
Capybara.using_session(Capybara.last_used_session) { super }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
module ClassMethods
|
|
28
|
-
# Configure better system tests for RSpec
|
|
29
|
-
# This sets up the necessary hooks and configuration
|
|
30
|
-
def configure_better_system_tests!
|
|
31
|
-
# Make urls in mailers contain the correct server host.
|
|
32
|
-
# This is required for testing links in emails (e.g., via capybara-email).
|
|
33
|
-
around(:each, type: :system) do |ex|
|
|
34
|
-
was_host = Rails.application.default_url_options[:host]
|
|
35
|
-
Rails.application.default_url_options[:host] = Capybara.server_host
|
|
36
|
-
ex.run
|
|
37
|
-
Rails.application.default_url_options[:host] = was_host
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Make sure this hook runs before others
|
|
41
|
-
# Means you don't have to set js: true in every system spec
|
|
42
|
-
prepend_before(:each, type: :system) do
|
|
43
|
-
driven_by :cuprite
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Enable automatic screenshots on failure
|
|
47
|
-
# Add CI-specific timeout and retry logic for form interactions
|
|
48
|
-
# Includes MultipleExceptionError detection
|
|
49
|
-
around(:each, type: :system) do |example|
|
|
50
|
-
exception = nil
|
|
51
|
-
|
|
52
|
-
begin
|
|
53
|
-
example.run
|
|
54
|
-
rescue => e
|
|
55
|
-
exception = e
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Also check example.exception in case RSpec aggregated exceptions there
|
|
59
|
-
exception ||= example.exception
|
|
60
|
-
|
|
61
|
-
# Handle MultipleExceptionError specially - don't retry, just report and skip
|
|
62
|
-
if exception.is_a?(RSpec::Core::MultipleExceptionError)
|
|
63
|
-
puts "\n" + ("=" * 80)
|
|
64
|
-
puts "⚠️ MULTIPLE EXCEPTIONS - SKIPPING TEST (NO RETRY)"
|
|
65
|
-
puts "=" * 80
|
|
66
|
-
puts "Test: #{example.full_description}"
|
|
67
|
-
puts "File: #{example.metadata[:file_path]}:#{example.metadata[:line_number]}"
|
|
68
|
-
puts "Total exceptions: #{exception.all_exceptions.count}"
|
|
69
|
-
puts "=" * 80
|
|
70
|
-
|
|
71
|
-
# Group exceptions by class for cleaner output
|
|
72
|
-
exceptions_by_class = exception.all_exceptions.group_by(&:class)
|
|
73
|
-
exceptions_by_class.each do |klass, exs|
|
|
74
|
-
puts "\n#{klass.name} (#{exs.count} occurrence#{"s" if exs.count > 1}):"
|
|
75
|
-
puts " #{exs.first.message.split("\n").first}"
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
puts "\n" + ("=" * 80)
|
|
79
|
-
puts "⚠️ Skipping retry - moving to next test"
|
|
80
|
-
puts "=" * 80 + "\n"
|
|
81
|
-
|
|
82
|
-
# Mark this so after hooks can skip verbose output
|
|
83
|
-
example.metadata[:multiple_exception_detected] = true
|
|
84
|
-
|
|
85
|
-
# Re-raise to mark test as failed, but don't retry
|
|
86
|
-
raise exception
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# For other exceptions in CI, log and re-raise
|
|
90
|
-
if exception && ENV["GITHUB_ACTIONS"] == "true"
|
|
91
|
-
puts "[CI] Test error detected: #{exception.class} - #{exception.message}"
|
|
92
|
-
puts "[CI] Current URL: #{begin
|
|
93
|
-
page.current_url
|
|
94
|
-
rescue
|
|
95
|
-
""
|
|
96
|
-
end}"
|
|
97
|
-
raise exception
|
|
98
|
-
elsif exception
|
|
99
|
-
# Not in CI, just re-raise
|
|
100
|
-
raise exception
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
after(:each, type: :system) do |example|
|
|
105
|
-
next unless example.exception
|
|
106
|
-
|
|
107
|
-
begin
|
|
108
|
-
# Wait for any pending JavaScript to complete
|
|
109
|
-
# Cuprite has direct network idle support
|
|
110
|
-
begin
|
|
111
|
-
page.driver.wait_for_network_idle
|
|
112
|
-
rescue
|
|
113
|
-
nil
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Wait for DOM to be ready
|
|
117
|
-
# sleep 0.5
|
|
118
|
-
|
|
119
|
-
# Get comprehensive page info
|
|
120
|
-
page_html = begin
|
|
121
|
-
page.html
|
|
122
|
-
rescue
|
|
123
|
-
""
|
|
124
|
-
end
|
|
125
|
-
current_url = begin
|
|
126
|
-
page.current_url
|
|
127
|
-
rescue
|
|
128
|
-
""
|
|
129
|
-
end
|
|
130
|
-
current_path = begin
|
|
131
|
-
page.current_path
|
|
132
|
-
rescue
|
|
133
|
-
""
|
|
134
|
-
end
|
|
135
|
-
page_title = begin
|
|
136
|
-
page.title
|
|
137
|
-
rescue
|
|
138
|
-
""
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Check for redirect or blank page indicators
|
|
142
|
-
if page_html.length < 100
|
|
143
|
-
puts "Warning: Page content appears minimal (#{page_html.length} chars) when taking screenshot"
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Use Capybara's save_screenshot method
|
|
147
|
-
screenshot_path = Capybara.save_screenshot
|
|
148
|
-
if screenshot_path
|
|
149
|
-
puts "Screenshot saved to: #{screenshot_path}"
|
|
150
|
-
puts "Page title: #{page_title}" if page_title.present?
|
|
151
|
-
puts "Current URL: #{current_url}" if current_url.present?
|
|
152
|
-
puts "Current path: #{current_path}" if current_path.present?
|
|
153
|
-
puts "Page content length: #{page_html.length} characters"
|
|
154
|
-
|
|
155
|
-
# Save page HTML for debugging in CI
|
|
156
|
-
if ENV["GITHUB_ACTIONS"] && page_html.present?
|
|
157
|
-
html_debug_path = screenshot_path.gsub(".png", ".html")
|
|
158
|
-
File.write(html_debug_path, page_html)
|
|
159
|
-
puts "Page HTML saved to: #{html_debug_path}"
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
rescue => e
|
|
163
|
-
puts "Failed to capture screenshot: #{e.message}"
|
|
164
|
-
# Skip verbose output if already handled by MultipleExceptionError handler
|
|
165
|
-
unless example.metadata[:multiple_exception_detected]
|
|
166
|
-
puts "Exception class: #{example.exception.class}"
|
|
167
|
-
puts "Exception message: #{example.exception.message}"
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def self.included(base)
|
|
175
|
-
base.extend(ClassMethods)
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
end
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Shared Capybara configuration for all Panda gems
|
|
4
|
-
# This provides standard Capybara setup with sensible defaults
|
|
5
|
-
|
|
6
|
-
# We don't want to hang around for too long for processes at the moment
|
|
7
|
-
Capybara.default_max_wait_time = ENV["CAPYBARA_MAX_WAIT_TIME"].present? ? ENV["CAPYBARA_MAX_WAIT_TIME"].to_i : 2
|
|
8
|
-
|
|
9
|
-
# Normalize whitespaces when using `has_text?` and similar matchers,
|
|
10
|
-
# i.e., ignore newlines, trailing spaces, etc.
|
|
11
|
-
# That makes tests less dependent on slight UI changes.
|
|
12
|
-
Capybara.default_normalize_ws = true
|
|
13
|
-
|
|
14
|
-
# Where to store system tests artifacts (e.g. screenshots, downloaded files, etc.).
|
|
15
|
-
# It could be useful to be able to configure this path from the outside (e.g., on CI).
|
|
16
|
-
Capybara.save_path = ENV.fetch("CAPYBARA_ARTIFACTS", "./tmp/capybara")
|
|
17
|
-
|
|
18
|
-
# Disable animation so we're not waiting for it
|
|
19
|
-
Capybara.disable_animation = true
|
|
20
|
-
|
|
21
|
-
# See SystemTestHelpers#take_screenshot
|
|
22
|
-
# This allows us to track which session was last used for proper screenshot naming
|
|
23
|
-
Capybara.singleton_class.prepend(Module.new do
|
|
24
|
-
attr_accessor :last_used_session
|
|
25
|
-
|
|
26
|
-
def using_session(name, &block)
|
|
27
|
-
self.last_used_session = name
|
|
28
|
-
super
|
|
29
|
-
ensure
|
|
30
|
-
self.last_used_session = nil
|
|
31
|
-
end
|
|
32
|
-
end)
|
|
33
|
-
|
|
34
|
-
# Configure server host and port
|
|
35
|
-
# This is loaded for all environments
|
|
36
|
-
# → you do not want fixed ports there
|
|
37
|
-
Capybara.server_host = "127.0.0.1"
|
|
38
|
-
Capybara.server_port = ENV["CAPYBARA_PORT"]&.to_i # Let Capybara choose if not specified
|
|
39
|
-
|
|
40
|
-
# Configure Puma server with explicit options
|
|
41
|
-
# Use single-threaded mode to share database connection with tests
|
|
42
|
-
Capybara.register_server :puma do |app, port, host|
|
|
43
|
-
require "rack/handler/puma"
|
|
44
|
-
Rack::Handler::Puma.run(app, Port: port, Host: host, Silent: true, Threads: "1:1")
|
|
45
|
-
end
|
|
46
|
-
Capybara.server = :puma
|
|
47
|
-
|
|
48
|
-
# Do not set app_host here - let Capybara determine it from the server
|
|
49
|
-
# This avoids conflicts between what's configured and what's actually running
|
|
50
|
-
|
|
51
|
-
# RSpec configuration for Capybara
|
|
52
|
-
RSpec.configure do |config|
|
|
53
|
-
# Save screenshots on system test failures
|
|
54
|
-
config.after(:each, type: :system) do |example|
|
|
55
|
-
if example.exception
|
|
56
|
-
timestamp = Time.now.strftime("%Y-%m-%d-%H%M%S")
|
|
57
|
-
"tmp/capybara/failures/#{example.full_description.parameterize}_#{timestamp}"
|
|
58
|
-
|
|
59
|
-
# Screenshots are saved automatically by Capybara, but we could save additional artifacts here
|
|
60
|
-
# save_page("#{filename_base}.html")
|
|
61
|
-
# save_screenshot("#{filename_base}.png", full: true)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# CI-specific Capybara configuration
|
|
4
|
-
# This file configures Capybara for GitHub Actions and other CI environments
|
|
5
|
-
# It uses a more robust Puma setup with compatibility for Puma 6 and 7+
|
|
6
|
-
|
|
7
|
-
return unless defined?(Capybara)
|
|
8
|
-
|
|
9
|
-
ci_mode = ENV["GITHUB_ACTIONS"] == "true" || ENV["CI_SYSTEM_SPECS"] == "true"
|
|
10
|
-
return unless ci_mode
|
|
11
|
-
|
|
12
|
-
require "rack/handler/puma"
|
|
13
|
-
|
|
14
|
-
RSpec.configure do |config|
|
|
15
|
-
config.before(:suite) do
|
|
16
|
-
Capybara.server = :puma_ci
|
|
17
|
-
Capybara.default_max_wait_time = Integer(ENV.fetch("CAPYBARA_MAX_WAIT_TIME", 5))
|
|
18
|
-
|
|
19
|
-
port = Integer(ENV.fetch("CAPYBARA_PORT", 3001))
|
|
20
|
-
Capybara.server_host = "127.0.0.1"
|
|
21
|
-
Capybara.server_port = port
|
|
22
|
-
Capybara.app_host = "http://127.0.0.1:#{port}"
|
|
23
|
-
Capybara.always_include_port = true
|
|
24
|
-
|
|
25
|
-
puts "[CI Config] Capybara.server = #{Capybara.server.inspect}"
|
|
26
|
-
puts "[CI Config] Capybara.app_host = #{Capybara.app_host.inspect}"
|
|
27
|
-
puts "[CI Config] Capybara.server_host = #{Capybara.server_host.inspect}"
|
|
28
|
-
puts "[CI Config] Capybara.server_port = #{Capybara.server_port.inspect}"
|
|
29
|
-
puts "[CI Config] Capybara.max_wait = #{Capybara.default_max_wait_time}s"
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Capybara.register_server :puma_ci do |app, port, host|
|
|
34
|
-
puts "[CI Config] Starting Puma (single mode) on #{host}:#{port}"
|
|
35
|
-
|
|
36
|
-
min_threads = Integer(ENV.fetch("PUMA_MIN_THREADS", "2"))
|
|
37
|
-
max_threads = Integer(ENV.fetch("PUMA_MAX_THREADS", "2"))
|
|
38
|
-
|
|
39
|
-
# Wrap the app to catch startup errors
|
|
40
|
-
wrapped_app = proc do |env|
|
|
41
|
-
app.call(env)
|
|
42
|
-
rescue => e
|
|
43
|
-
puts "[CI ERROR] Rails middleware error during request:"
|
|
44
|
-
puts "[CI ERROR] #{e.class}: #{e.message}"
|
|
45
|
-
puts "[CI ERROR] Backtrace:"
|
|
46
|
-
puts e.backtrace.first(10).join("\n")
|
|
47
|
-
raise e
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Allow conditional silencing of server logs via CAPYBARA_SERVER_VERBOSE
|
|
51
|
-
verbose = ENV.fetch("CAPYBARA_SERVER_VERBOSE", "false") == "true"
|
|
52
|
-
|
|
53
|
-
options = {
|
|
54
|
-
Host: host,
|
|
55
|
-
Port: port,
|
|
56
|
-
Threads: "#{min_threads}:#{max_threads}",
|
|
57
|
-
Workers: 0,
|
|
58
|
-
Silent: !verbose,
|
|
59
|
-
Verbose: verbose,
|
|
60
|
-
PreloadApp: false
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
# --- Puma Compatibility Layer (supports Puma 6 AND Puma 7+) ---
|
|
64
|
-
puma_run = Rack::Handler::Puma.method(:run)
|
|
65
|
-
|
|
66
|
-
if puma_run.arity == 2
|
|
67
|
-
# Puma <= 6.x signature:
|
|
68
|
-
# run(app, options_hash)
|
|
69
|
-
puts "[CI Config] Using Puma <= 6 API (arity 2)"
|
|
70
|
-
Rack::Handler::Puma.run(wrapped_app, options)
|
|
71
|
-
else
|
|
72
|
-
# Puma >= 7.x signature:
|
|
73
|
-
# run(app, **options)
|
|
74
|
-
puts "[CI Config] Using Puma >= 7 API (keyword args)"
|
|
75
|
-
Rack::Handler::Puma.run(wrapped_app, **options)
|
|
76
|
-
end
|
|
77
|
-
end
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-leading:initial;--tw-duration:initial;--tw-font-weight:initial;--tw-space-y-reverse:0;--tw-tracking:initial;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-serif:ui-serif,Georgia,Cambria,"Times New Roman",Times,serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-indigo-500:var(--color-primary-500);--color-indigo-600:var(--color-primary-600);--color-gray-50:var(--color-gray-50);--color-gray-100:var(--color-gray-100);--color-gray-200:var(--color-gray-200);--color-gray-300:var(--color-gray-300);--color-gray-400:var(--color-gray-400);--color-gray-500:var(--color-gray-500);--color-gray-600:var(--color-gray-600);--color-gray-700:var(--color-gray-700);--color-gray-800:var(--color-gray-800);--color-gray-900:var(--color-gray-900);--color-gray-950:var(--color-gray-950);--color-black:var(--color-black);--color-white:var(--color-white);--spacing:.25rem;--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height:calc(1.5/1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25/1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-light:var(--color-light);--color-mid:var(--color-mid);--color-dark:var(--color-dark);--color-highlight:var(--color-highlight);--color-active:var(--color-active);--color-inactive:var(--color-inactive);--color-warning:var(--color-warning);--color-error:var(--color-error);--color-primary-50:var(--color-primary-50);--color-primary-100:var(--color-primary-100);--color-primary-200:var(--color-primary-200);--color-primary-300:var(--color-primary-300);--color-primary-400:var(--color-primary-400);--color-primary-500:var(--color-primary-500);--color-primary-600:var(--color-primary-600);--color-primary-700:var(--color-primary-700);--color-primary-800:var(--color-primary-800);--color-primary-900:var(--color-primary-900);--color-primary-950:var(--color-primary-950)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html[data-theme=default]{--color-white:#f9f9f9;--color-black:#1a161d;--color-light:#eecee6;--color-mid:#8d5eb7;--color-dark:#211d49;--color-highlight:#d04014;--color-active:#008755;--color-warning:#facf8e;--color-inactive:#d8f7f5;--color-error:#f58181;--color-primary-50:#faf5ff;--color-primary-100:#f3e8ff;--color-primary-200:#e9d5ff;--color-primary-300:#d8b4fe;--color-primary-400:#c084fc;--color-primary-500:#8d5eb7;--color-primary-600:#7850a0;--color-primary-700:#64418c;--color-primary-800:#3c2d64;--color-primary-900:#211d49;--color-primary-950:#181432;--color-gray-50:#f9fafb;--color-gray-100:#f3f4f6;--color-gray-200:#e5e7eb;--color-gray-300:#d1d5db;--color-gray-400:#9ca3af;--color-gray-500:#6b7280;--color-gray-600:#4b5563;--color-gray-700:#374151;--color-gray-800:#1f2937;--color-gray-900:#111827;--color-gray-950:#030712}html[data-theme=sky]{--color-white:#f9f9f9;--color-black:#1a161d;--color-light:#cceef2;--color-mid:#2a669f;--color-dark:#14204a;--color-highlight:#d04014;--color-active:#459a59;--color-warning:#f4be66;--color-inactive:#d8f7f5;--color-error:#d04014;--color-primary-50:#f0f9ff;--color-primary-100:#e0f2fe;--color-primary-200:#bae6fd;--color-primary-300:#7dd3fc;--color-primary-400:#38bdf8;--color-primary-500:#2a669f;--color-primary-600:#235584;--color-primary-700:#1c446a;--color-primary-800:#163452;--color-primary-900:#14204a;--color-primary-950:#0f1837;--color-gray-50:#f9fafb;--color-gray-100:#f3f4f6;--color-gray-200:#e5e7eb;--color-gray-300:#d1d5db;--color-gray-400:#9ca3af;--color-gray-500:#6b7280;--color-gray-600:#4b5563;--color-gray-700:#374151;--color-gray-800:#1f2937;--color-gray-900:#111827;--color-gray-950:#030712}a.block-link:after{content:"";position:absolute;inset:0}html[data-theme=default] .bg-gradient-admin{background:linear-gradient(to bottom right,#211d49,#8d5eb7)}html[data-theme=sky] .bg-gradient-admin{background:linear-gradient(to bottom right,#14204a,#2a669f)}}@layer components{input[type=text],input[type=email],input[type=password],input[type=url],input[type=tel],input[type=number],input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time],input[type=search],textarea{border-radius:var(--radius-md);border-style:var(--tw-border-style);background-color:var(--color-white);width:100%;padding:calc(var(--spacing)*2);color:var(--color-gray-900);--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-mid);--tw-ring-inset:inset;border-width:0;display:block}:is(input[type=text],input[type=email],input[type=password],input[type=url],input[type=tel],input[type=number],input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time],input[type=search],textarea)::placeholder{color:var(--color-gray-300)}:is(input[type=text],input[type=email],input[type=password],input[type=url],input[type=tel],input[type=number],input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time],input[type=search],textarea):focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-dark);--tw-ring-inset:inset}@media (hover:hover){:is(input[type=text],input[type=email],input[type=password],input[type=url],input[type=tel],input[type=number],input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time],input[type=search],textarea):hover{cursor:pointer}}@media (min-width:40rem){input[type=text],input[type=email],input[type=password],input[type=url],input[type=tel],input[type=number],input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time],input[type=search],textarea{--tw-leading:calc(var(--spacing)*6);line-height:calc(var(--spacing)*6)}}:is(input[type=text],input[type=email],input[type=password],input[type=url],input[type=tel],input[type=number],input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time],input[type=search],textarea):disabled{cursor:not-allowed;background-color:var(--color-gray-50);--tw-ring-color:var(--color-gray-300)}:is(input[type=text],input[type=email],input[type=password],input[type=url],input[type=tel],input[type=number],input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time],input[type=search],textarea):disabled:focus{--tw-ring-color:var(--color-gray-300)}select{border-radius:var(--radius-md);border-style:var(--tw-border-style);background-color:var(--color-white);width:100%;padding-block:calc(var(--spacing)*1.5);padding-right:calc(var(--spacing)*10);padding-left:calc(var(--spacing)*3);color:var(--color-gray-900);--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-mid);--tw-ring-inset:inset;border-width:0;display:block}select:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-dark);--tw-ring-inset:inset}@media (hover:hover){select:hover{cursor:pointer}}@media (min-width:40rem){select{--tw-leading:calc(var(--spacing)*6);line-height:calc(var(--spacing)*6)}}select:disabled{cursor:not-allowed;background-color:var(--color-gray-50);--tw-ring-color:var(--color-gray-300)}select:disabled:focus{--tw-ring-color:var(--color-gray-300)}select{appearance:none;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em}input[type=checkbox],input[type=radio]{height:calc(var(--spacing)*4);width:calc(var(--spacing)*4);border-color:var(--color-gray-300);background-color:var(--color-white);color:var(--color-indigo-600);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;border-radius:.25rem;transition-duration:.2s}:is(input[type=checkbox],input[type=radio]):focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-indigo-500);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}:is(input[type=checkbox],input[type=radio]):disabled{cursor:not-allowed;background-color:var(--color-gray-100)}input[type=radio]{border-radius:3.40282e38px}label{margin-bottom:calc(var(--spacing)*1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);display:block}.field{margin-bottom:calc(var(--spacing)*4)}input.error,textarea.error,select.error,:is(input.error,textarea.error,select.error):focus{--tw-ring-color:var(--color-red-500)}.field-error{margin-top:calc(var(--spacing)*1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-600)}.btn{border-radius:var(--radius-md);padding-inline:calc(var(--spacing)*6);padding-block:calc(var(--spacing)*3);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;justify-content:center;align-items:center;transition-duration:.2s;display:inline-flex}.btn:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.btn:disabled{cursor:not-allowed;opacity:.5}.btn-primary{background-color:var(--color-mid);color:var(--color-white)}@media (hover:hover){.btn-primary:hover{opacity:.9}}.btn-primary:focus{--tw-ring-color:var(--color-mid)}.btn-secondary{background-color:var(--color-gray-200);color:var(--color-gray-900)}@media (hover:hover){.btn-secondary:hover{background-color:var(--color-gray-300)}}.btn-secondary:focus{--tw-ring-color:var(--color-gray-500)}.btn-danger{background-color:var(--color-red-600);color:var(--color-white)}@media (hover:hover){.btn-danger:hover{background-color:var(--color-red-700)}}.btn-danger:focus{--tw-ring-color:var(--color-red-500)}a.block-link:after{content:"";position:absolute;inset:0}:where(.codex-editor__redactor .ce-block .ce-block__content>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(1.6rem*var(--tw-space-y-reverse));margin-block-end:calc(1.6rem*calc(1 - var(--tw-space-y-reverse)))}.codex-editor__redactor .ce-block .ce-block__content{font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-leading:1.6;--tw-font-weight:var(--font-weight-normal);line-height:1.6;font-weight:var(--font-weight-normal);color:var(--color-dark)}.codex-editor__redactor .ce-block .ce-block__content h1.ce-header{font-family:var(--font-sans);font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height));--tw-leading:1.2;--tw-font-weight:var(--font-weight-semibold);line-height:1.2;font-weight:var(--font-weight-semibold)}@media (min-width:48rem){.codex-editor__redactor .ce-block .ce-block__content h1.ce-header{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}}.codex-editor__redactor .ce-block .ce-block__content h2.ce-header{margin-top:calc(var(--spacing)*8);margin-bottom:calc(var(--spacing)*4);font-family:var(--font-sans);font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height));--tw-leading:1.3;--tw-font-weight:var(--font-weight-medium);line-height:1.3;font-weight:var(--font-weight-medium)}.codex-editor__redactor .ce-block .ce-block__content h3.ce-header{margin-top:calc(var(--spacing)*6);margin-bottom:calc(var(--spacing)*4);font-family:var(--font-sans);font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-leading:1.3;--tw-font-weight:var(--font-weight-normal);line-height:1.3;font-weight:var(--font-weight-normal)}.codex-editor__redactor .ce-block .ce-block__content p,.codex-editor__redactor .ce-block .ce-block__content li{--tw-leading:1.6;--tw-tracking:var(--tracking-wide);max-width:85ch;letter-spacing:var(--tracking-wide);line-height:1.6}:is(.codex-editor__redactor .ce-block .ce-block__content p,.codex-editor__redactor .ce-block .ce-block__content li) a{color:#1a9597;text-underline-offset:2px;text-decoration-line:underline}@media (hover:hover){:is(.codex-editor__redactor .ce-block .ce-block__content p,.codex-editor__redactor .ce-block .ce-block__content li) a:hover{color:#158486}}:is(.codex-editor__redactor .ce-block .ce-block__content p,.codex-editor__redactor .ce-block .ce-block__content li) a:focus{outline-style:var(--tw-outline-style);outline-offset:2px;outline-width:2px;outline-color:#1a9597}:is(.codex-editor__redactor .ce-block .ce-block__content p,.codex-editor__redactor .ce-block .ce-block__content li) strong,:is(.codex-editor__redactor .ce-block .ce-block__content p,.codex-editor__redactor .ce-block .ce-block__content li) b{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.codex-editor__redactor .ce-block .ce-block__content p{margin-bottom:calc(var(--spacing)*4)}.codex-editor__redactor .ce-block .ce-block__content .cdx-quote{margin-bottom:calc(var(--spacing)*4);border-left-style:var(--tw-border-style);border-left-width:8px;border-left-color:var(--color-inactive);padding:calc(var(--spacing)*6);background-color:#eef0f3}.codex-editor__redactor .ce-block .ce-block__content .cdx-quote .cdx-quote__caption{margin-top:calc(var(--spacing)*2);margin-left:calc(var(--spacing)*6);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-dark);display:block}.codex-editor__redactor .ce-block .ce-block__content .cdx-quote .cdx-quote__text{quotes:"“" "”" "‘" "’";padding-left:calc(var(--spacing)*6)}.codex-editor__redactor .ce-block .ce-block__content .cdx-quote .cdx-quote__text:before{margin-right:calc(var(--spacing)*2);margin-left:calc(var(--spacing)*-8);vertical-align:text-bottom;font-family:var(--font-serif);font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height));--tw-leading:calc(var(--spacing)*4);line-height:calc(var(--spacing)*4);color:var(--color-dark);content:open-quote}.codex-editor__redactor .ce-block .ce-block__content .cdx-quote .cdx-quote__text p{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));font-style:italic;display:inline}.codex-editor__redactor .ce-block .ce-block__content .cdx-list{margin-bottom:calc(var(--spacing)*4);padding-left:calc(var(--spacing)*6)}--ordered:is(.codex-editor__redactor .ce-block .ce-block__content .cdx-list){list-style-type:decimal}--unordered:is(.codex-editor__redactor .ce-block .ce-block__content .cdx-list){list-style-type:disc}.codex-editor__redactor .ce-block .ce-block__content .cdx-list .cdx-list{margin-top:calc(var(--spacing)*2);margin-bottom:calc(var(--spacing)*0)}.codex-editor__redactor .ce-block .ce-block__content .cdx-list .cdx-list__item{margin-bottom:calc(var(--spacing)*2);padding-left:calc(var(--spacing)*2)}.codex-editor__redactor .ce-block .ce-block__content .cdx-nested-list{margin-bottom:calc(var(--spacing)*4);padding-left:calc(var(--spacing)*6)}--ordered:is(.codex-editor__redactor .ce-block .ce-block__content .cdx-nested-list){list-style-type:decimal}--unordered:is(.codex-editor__redactor .ce-block .ce-block__content .cdx-nested-list){list-style-type:disc}.codex-editor__redactor .ce-block .ce-block__content .cdx-nested-list .cdx-nested-list{margin-top:calc(var(--spacing)*2);margin-bottom:calc(var(--spacing)*0)}.codex-editor__redactor .ce-block .ce-block__content .cdx-nested-list .cdx-nested-list__item{margin-bottom:calc(var(--spacing)*2);padding-left:calc(var(--spacing)*2)}.codex-editor__redactor .ce-block .ce-block__content .cdx-table{margin-block:calc(var(--spacing)*6);border-collapse:collapse;border-style:var(--tw-border-style);border-width:2px;border-color:var(--color-dark);width:100%}__head:is(.codex-editor__redactor .ce-block .ce-block__content .cdx-table){border-right-style:var(--tw-border-style);border-right-width:2px;border-color:var(--color-dark);background-color:var(--color-light);padding:calc(var(--spacing)*3);--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}__row:is(.codex-editor__redactor .ce-block .ce-block__content .cdx-table){border-bottom-style:var(--tw-border-style);border-bottom-width:2px;border-color:var(--color-dark)}__cell:is(.codex-editor__redactor .ce-block .ce-block__content .cdx-table){border-right-style:var(--tw-border-style);border-right-width:2px;border-color:var(--color-dark);padding:calc(var(--spacing)*3)}.codex-editor__redactor .ce-block .ce-block__content .cdx-embed iframe{--tw-border-style:none;border-style:none;width:100%}}@layer utilities;@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-leading{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}
|