aimodelshare 0.3.7__py3-none-any.whl

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.
Files changed (171) hide show
  1. aimodelshare/README.md +26 -0
  2. aimodelshare/__init__.py +100 -0
  3. aimodelshare/aimsonnx.py +2381 -0
  4. aimodelshare/api.py +836 -0
  5. aimodelshare/auth.py +163 -0
  6. aimodelshare/aws.py +511 -0
  7. aimodelshare/aws_client.py +173 -0
  8. aimodelshare/base_image.py +154 -0
  9. aimodelshare/bucketpolicy.py +106 -0
  10. aimodelshare/color_mappings/color_mapping_keras.csv +121 -0
  11. aimodelshare/color_mappings/color_mapping_pytorch.csv +117 -0
  12. aimodelshare/containerisation.py +244 -0
  13. aimodelshare/containerization.py +712 -0
  14. aimodelshare/containerization_templates/Dockerfile.txt +8 -0
  15. aimodelshare/containerization_templates/Dockerfile_PySpark.txt +23 -0
  16. aimodelshare/containerization_templates/buildspec.txt +14 -0
  17. aimodelshare/containerization_templates/lambda_function.txt +40 -0
  18. aimodelshare/custom_approach/__init__.py +1 -0
  19. aimodelshare/custom_approach/lambda_function.py +17 -0
  20. aimodelshare/custom_eval_metrics.py +103 -0
  21. aimodelshare/data_sharing/__init__.py +0 -0
  22. aimodelshare/data_sharing/data_sharing_templates/Dockerfile.txt +3 -0
  23. aimodelshare/data_sharing/data_sharing_templates/__init__.py +1 -0
  24. aimodelshare/data_sharing/data_sharing_templates/buildspec.txt +15 -0
  25. aimodelshare/data_sharing/data_sharing_templates/codebuild_policies.txt +129 -0
  26. aimodelshare/data_sharing/data_sharing_templates/codebuild_trust_relationship.txt +12 -0
  27. aimodelshare/data_sharing/download_data.py +620 -0
  28. aimodelshare/data_sharing/share_data.py +373 -0
  29. aimodelshare/data_sharing/utils.py +8 -0
  30. aimodelshare/deploy_custom_lambda.py +246 -0
  31. aimodelshare/documentation/Makefile +20 -0
  32. aimodelshare/documentation/karma_sphinx_theme/__init__.py +28 -0
  33. aimodelshare/documentation/karma_sphinx_theme/_version.py +2 -0
  34. aimodelshare/documentation/karma_sphinx_theme/breadcrumbs.html +70 -0
  35. aimodelshare/documentation/karma_sphinx_theme/layout.html +172 -0
  36. aimodelshare/documentation/karma_sphinx_theme/search.html +50 -0
  37. aimodelshare/documentation/karma_sphinx_theme/searchbox.html +14 -0
  38. aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css +2 -0
  39. aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css.map +1 -0
  40. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css +2751 -0
  41. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css.map +1 -0
  42. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css +2 -0
  43. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css.map +1 -0
  44. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.eot +0 -0
  45. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.svg +32 -0
  46. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.ttf +0 -0
  47. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff +0 -0
  48. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff2 +0 -0
  49. aimodelshare/documentation/karma_sphinx_theme/static/js/theme.js +68 -0
  50. aimodelshare/documentation/karma_sphinx_theme/theme.conf +9 -0
  51. aimodelshare/documentation/make.bat +35 -0
  52. aimodelshare/documentation/requirements.txt +2 -0
  53. aimodelshare/documentation/source/about.rst +18 -0
  54. aimodelshare/documentation/source/advanced_features.rst +137 -0
  55. aimodelshare/documentation/source/competition.rst +218 -0
  56. aimodelshare/documentation/source/conf.py +58 -0
  57. aimodelshare/documentation/source/create_credentials.rst +86 -0
  58. aimodelshare/documentation/source/example_notebooks.rst +132 -0
  59. aimodelshare/documentation/source/functions.rst +151 -0
  60. aimodelshare/documentation/source/gettingstarted.rst +390 -0
  61. aimodelshare/documentation/source/images/creds1.png +0 -0
  62. aimodelshare/documentation/source/images/creds2.png +0 -0
  63. aimodelshare/documentation/source/images/creds3.png +0 -0
  64. aimodelshare/documentation/source/images/creds4.png +0 -0
  65. aimodelshare/documentation/source/images/creds5.png +0 -0
  66. aimodelshare/documentation/source/images/creds_file_example.png +0 -0
  67. aimodelshare/documentation/source/images/predict_tab.png +0 -0
  68. aimodelshare/documentation/source/index.rst +110 -0
  69. aimodelshare/documentation/source/modelplayground.rst +132 -0
  70. aimodelshare/exceptions.py +11 -0
  71. aimodelshare/generatemodelapi.py +1270 -0
  72. aimodelshare/iam/codebuild_policy.txt +129 -0
  73. aimodelshare/iam/codebuild_trust_relationship.txt +12 -0
  74. aimodelshare/iam/lambda_policy.txt +15 -0
  75. aimodelshare/iam/lambda_trust_relationship.txt +12 -0
  76. aimodelshare/json_templates/__init__.py +1 -0
  77. aimodelshare/json_templates/api_json.txt +155 -0
  78. aimodelshare/json_templates/auth/policy.txt +1 -0
  79. aimodelshare/json_templates/auth/role.txt +1 -0
  80. aimodelshare/json_templates/eval/policy.txt +1 -0
  81. aimodelshare/json_templates/eval/role.txt +1 -0
  82. aimodelshare/json_templates/function/policy.txt +1 -0
  83. aimodelshare/json_templates/function/role.txt +1 -0
  84. aimodelshare/json_templates/integration_response.txt +5 -0
  85. aimodelshare/json_templates/lambda_policy_1.txt +15 -0
  86. aimodelshare/json_templates/lambda_policy_2.txt +8 -0
  87. aimodelshare/json_templates/lambda_role_1.txt +12 -0
  88. aimodelshare/json_templates/lambda_role_2.txt +16 -0
  89. aimodelshare/leaderboard.py +174 -0
  90. aimodelshare/main/1.txt +132 -0
  91. aimodelshare/main/1B.txt +112 -0
  92. aimodelshare/main/2.txt +153 -0
  93. aimodelshare/main/3.txt +134 -0
  94. aimodelshare/main/4.txt +128 -0
  95. aimodelshare/main/5.txt +109 -0
  96. aimodelshare/main/6.txt +105 -0
  97. aimodelshare/main/7.txt +144 -0
  98. aimodelshare/main/8.txt +142 -0
  99. aimodelshare/main/__init__.py +1 -0
  100. aimodelshare/main/authorization.txt +275 -0
  101. aimodelshare/main/eval_classification.txt +79 -0
  102. aimodelshare/main/eval_lambda.txt +1709 -0
  103. aimodelshare/main/eval_regression.txt +80 -0
  104. aimodelshare/main/lambda_function.txt +8 -0
  105. aimodelshare/main/nst.txt +149 -0
  106. aimodelshare/model.py +1543 -0
  107. aimodelshare/modeluser.py +215 -0
  108. aimodelshare/moral_compass/README.md +408 -0
  109. aimodelshare/moral_compass/__init__.py +65 -0
  110. aimodelshare/moral_compass/_version.py +3 -0
  111. aimodelshare/moral_compass/api_client.py +601 -0
  112. aimodelshare/moral_compass/apps/__init__.py +69 -0
  113. aimodelshare/moral_compass/apps/ai_consequences.py +540 -0
  114. aimodelshare/moral_compass/apps/bias_detective.py +714 -0
  115. aimodelshare/moral_compass/apps/ethical_revelation.py +898 -0
  116. aimodelshare/moral_compass/apps/fairness_fixer.py +889 -0
  117. aimodelshare/moral_compass/apps/judge.py +888 -0
  118. aimodelshare/moral_compass/apps/justice_equity_upgrade.py +853 -0
  119. aimodelshare/moral_compass/apps/mc_integration_helpers.py +820 -0
  120. aimodelshare/moral_compass/apps/model_building_game.py +1104 -0
  121. aimodelshare/moral_compass/apps/model_building_game_beginner.py +687 -0
  122. aimodelshare/moral_compass/apps/moral_compass_challenge.py +858 -0
  123. aimodelshare/moral_compass/apps/session_auth.py +254 -0
  124. aimodelshare/moral_compass/apps/shared_activity_styles.css +349 -0
  125. aimodelshare/moral_compass/apps/tutorial.py +481 -0
  126. aimodelshare/moral_compass/apps/what_is_ai.py +853 -0
  127. aimodelshare/moral_compass/challenge.py +365 -0
  128. aimodelshare/moral_compass/config.py +187 -0
  129. aimodelshare/placeholders/model.onnx +0 -0
  130. aimodelshare/placeholders/preprocessor.zip +0 -0
  131. aimodelshare/playground.py +1968 -0
  132. aimodelshare/postprocessormodules.py +157 -0
  133. aimodelshare/preprocessormodules.py +373 -0
  134. aimodelshare/pyspark/1.txt +195 -0
  135. aimodelshare/pyspark/1B.txt +181 -0
  136. aimodelshare/pyspark/2.txt +220 -0
  137. aimodelshare/pyspark/3.txt +204 -0
  138. aimodelshare/pyspark/4.txt +187 -0
  139. aimodelshare/pyspark/5.txt +178 -0
  140. aimodelshare/pyspark/6.txt +174 -0
  141. aimodelshare/pyspark/7.txt +211 -0
  142. aimodelshare/pyspark/8.txt +206 -0
  143. aimodelshare/pyspark/__init__.py +1 -0
  144. aimodelshare/pyspark/authorization.txt +258 -0
  145. aimodelshare/pyspark/eval_classification.txt +79 -0
  146. aimodelshare/pyspark/eval_lambda.txt +1441 -0
  147. aimodelshare/pyspark/eval_regression.txt +80 -0
  148. aimodelshare/pyspark/lambda_function.txt +8 -0
  149. aimodelshare/pyspark/nst.txt +213 -0
  150. aimodelshare/python/my_preprocessor.py +58 -0
  151. aimodelshare/readme.md +26 -0
  152. aimodelshare/reproducibility.py +181 -0
  153. aimodelshare/sam/Dockerfile.txt +8 -0
  154. aimodelshare/sam/Dockerfile_PySpark.txt +24 -0
  155. aimodelshare/sam/__init__.py +1 -0
  156. aimodelshare/sam/buildspec.txt +11 -0
  157. aimodelshare/sam/codebuild_policies.txt +129 -0
  158. aimodelshare/sam/codebuild_trust_relationship.txt +12 -0
  159. aimodelshare/sam/codepipeline_policies.txt +173 -0
  160. aimodelshare/sam/codepipeline_trust_relationship.txt +12 -0
  161. aimodelshare/sam/spark-class.txt +2 -0
  162. aimodelshare/sam/template.txt +54 -0
  163. aimodelshare/tools.py +103 -0
  164. aimodelshare/utils/__init__.py +78 -0
  165. aimodelshare/utils/optional_deps.py +38 -0
  166. aimodelshare/utils.py +57 -0
  167. aimodelshare-0.3.7.dist-info/METADATA +298 -0
  168. aimodelshare-0.3.7.dist-info/RECORD +171 -0
  169. aimodelshare-0.3.7.dist-info/WHEEL +5 -0
  170. aimodelshare-0.3.7.dist-info/licenses/LICENSE +5 -0
  171. aimodelshare-0.3.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,254 @@
1
+ """
2
+ Session-based authentication helpers for multi-user Gradio apps in Cloud Run.
3
+
4
+ This module provides utilities for managing per-session authentication state
5
+ instead of using global environment variables, which are unsafe in multi-user
6
+ Cloud Run deployments where multiple users share the same container instance.
7
+
8
+ Key Design Principles:
9
+ - All authentication state is stored in Gradio State objects (per-session)
10
+ - No usage of os.environ for username, password, or tokens
11
+ - Thread-safe token generation
12
+ - Backward compatible with existing get_aws_token() function
13
+ """
14
+
15
+ import logging
16
+ from typing import Optional, Dict, Any, Tuple
17
+ import os
18
+
19
+ # Import dependencies at module level
20
+ try:
21
+ import botocore.config
22
+ import boto3
23
+ from aimodelshare.exceptions import AuthorizationError
24
+ except ImportError as e:
25
+ # These are required dependencies
26
+ raise ImportError(
27
+ "Required dependencies not found. Ensure boto3 and botocore are installed."
28
+ ) from e
29
+
30
+ logger = logging.getLogger("aimodelshare.moral_compass.apps.session_auth")
31
+
32
+
33
+ def generate_auth_token(username: str, password: str) -> str:
34
+ """
35
+ Generate an authentication token for a user session.
36
+
37
+ This function wraps the existing get_aws_token() function from aimodelshare.aws
38
+ but accepts username and password as parameters instead of reading from
39
+ environment variables. This makes it safe for multi-user Cloud Run deployments.
40
+
41
+ Args:
42
+ username: The user's username
43
+ password: The user's password
44
+
45
+ Returns:
46
+ str: The AWS authentication token (IdToken from Cognito)
47
+
48
+ Raises:
49
+ AuthorizationError: If authentication fails
50
+ ValueError: If username or password is empty
51
+
52
+ Example:
53
+ >>> token = generate_auth_token("myuser", "mypass")
54
+ >>> # Store token in Gradio State, not in os.environ
55
+ """
56
+ if not username or not username.strip():
57
+ raise ValueError("Username cannot be empty")
58
+
59
+ if not password or not password.strip():
60
+ raise ValueError("Password cannot be empty")
61
+
62
+ try:
63
+ # Get Cognito client ID from environment or use default
64
+ # Note: This default is for the shared modelshare.ai Cognito pool
65
+ client_id = os.getenv('COGNITO_CLIENT_ID', '7ptv9f8pt36elmg0e4v9v7jo9t')
66
+ region = os.getenv('COGNITO_REGION', 'us-east-2')
67
+
68
+ # Create unsigned config for Cognito client
69
+ config = botocore.config.Config(signature_version=botocore.UNSIGNED)
70
+
71
+ # Initialize Cognito provider client
72
+ provider_client = boto3.client(
73
+ "cognito-idp",
74
+ region_name=region,
75
+ config=config
76
+ )
77
+
78
+ # Authenticate with Cognito using USER_PASSWORD_AUTH flow
79
+ response = provider_client.initiate_auth(
80
+ ClientId=client_id,
81
+ AuthFlow="USER_PASSWORD_AUTH",
82
+ AuthParameters={
83
+ "USERNAME": username.strip(),
84
+ "PASSWORD": password.strip()
85
+ },
86
+ )
87
+
88
+ # Extract IdToken from response
89
+ token = response["AuthenticationResult"]["IdToken"]
90
+
91
+ logger.info(f"Successfully generated auth token for user: {username}")
92
+ return token
93
+
94
+ except Exception as err:
95
+ logger.error(f"Authentication failed for user {username}: {err}")
96
+ raise AuthorizationError(f"Could not authorize user. {str(err)}")
97
+
98
+
99
+ def create_session_state() -> Dict[str, Any]:
100
+ """
101
+ Create an initial session state dictionary for authentication.
102
+
103
+ This state object should be stored in a Gradio State component and
104
+ passed through all functions that need authentication information.
105
+
106
+ Returns:
107
+ Dict containing:
108
+ - 'username': str or None
109
+ - 'token': str or None
110
+ - 'team_name': str or None
111
+ - 'is_authenticated': bool
112
+
113
+ Example:
114
+ >>> session_state = gr.State(value=create_session_state())
115
+ """
116
+ return {
117
+ 'username': None,
118
+ 'token': None,
119
+ 'team_name': None,
120
+ 'is_authenticated': False
121
+ }
122
+
123
+
124
+ def authenticate_session(
125
+ session_state: Dict[str, Any],
126
+ username: str,
127
+ password: str
128
+ ) -> Tuple[Dict[str, Any], bool, str]:
129
+ """
130
+ Authenticate a user and update the session state.
131
+
132
+ Args:
133
+ session_state: Current session state dictionary
134
+ username: Username to authenticate
135
+ password: Password to authenticate
136
+
137
+ Returns:
138
+ Tuple of (updated_session_state, success, message)
139
+ - updated_session_state: New session state with auth info
140
+ - success: True if authentication succeeded
141
+ - message: User-friendly message about authentication result
142
+
143
+ Example:
144
+ >>> session_state = create_session_state()
145
+ >>> new_state, success, msg = authenticate_session(
146
+ ... session_state, "myuser", "mypass"
147
+ ... )
148
+ >>> if success:
149
+ ... print(f"Logged in as {new_state['username']}")
150
+ """
151
+ try:
152
+ # Generate token
153
+ token = generate_auth_token(username, password)
154
+
155
+ # Update session state
156
+ new_state = session_state.copy()
157
+ new_state['username'] = username.strip()
158
+ new_state['token'] = token
159
+ new_state['is_authenticated'] = True
160
+
161
+ message = f"✓ Successfully authenticated as {username}"
162
+ logger.info(f"Session authenticated for user: {username}")
163
+
164
+ return new_state, True, message
165
+
166
+ except Exception as e:
167
+ # Authentication failed - don't update state
168
+ error_msg = f"⚠️ Authentication failed: {str(e)}"
169
+ logger.error(f"Session authentication failed for user {username}: {e}")
170
+
171
+ return session_state, False, error_msg
172
+
173
+
174
+ def get_session_token(session_state: Dict[str, Any]) -> Optional[str]:
175
+ """
176
+ Get the authentication token from session state.
177
+
178
+ Args:
179
+ session_state: Current session state dictionary
180
+
181
+ Returns:
182
+ The authentication token, or None if not authenticated
183
+
184
+ Example:
185
+ >>> token = get_session_token(session_state)
186
+ >>> if token:
187
+ ... # User is authenticated, proceed with API call
188
+ ... api_client.set_token(token)
189
+ """
190
+ if session_state and session_state.get('is_authenticated'):
191
+ return session_state.get('token')
192
+ return None
193
+
194
+
195
+ def get_session_username(session_state: Dict[str, Any]) -> Optional[str]:
196
+ """
197
+ Get the username from session state.
198
+
199
+ Args:
200
+ session_state: Current session state dictionary
201
+
202
+ Returns:
203
+ The username, or None if not authenticated
204
+ """
205
+ if session_state and session_state.get('is_authenticated'):
206
+ return session_state.get('username')
207
+ return None
208
+
209
+
210
+ def is_session_authenticated(session_state: Dict[str, Any]) -> bool:
211
+ """
212
+ Check if the session is authenticated.
213
+
214
+ Args:
215
+ session_state: Current session state dictionary
216
+
217
+ Returns:
218
+ True if session is authenticated, False otherwise
219
+ """
220
+ return bool(session_state and session_state.get('is_authenticated'))
221
+
222
+
223
+ def set_session_team(
224
+ session_state: Dict[str, Any],
225
+ team_name: str
226
+ ) -> Dict[str, Any]:
227
+ """
228
+ Set the team name in session state.
229
+
230
+ Args:
231
+ session_state: Current session state dictionary
232
+ team_name: Team name to assign
233
+
234
+ Returns:
235
+ Updated session state dictionary
236
+ """
237
+ new_state = session_state.copy()
238
+ new_state['team_name'] = team_name
239
+ return new_state
240
+
241
+
242
+ def get_session_team(session_state: Dict[str, Any]) -> Optional[str]:
243
+ """
244
+ Get the team name from session state.
245
+
246
+ Args:
247
+ session_state: Current session state dictionary
248
+
249
+ Returns:
250
+ The team name, or None if not set
251
+ """
252
+ if session_state:
253
+ return session_state.get('team_name')
254
+ return None
@@ -0,0 +1,349 @@
1
+ /*
2
+ * Shared Activity Styles for Activities 7, 8, and 9
3
+ *
4
+ * Consistent styling aligned with model_building_game.py patterns
5
+ */
6
+
7
+ /* ============================================================================
8
+ KPI Cards (Moral Compass widgets, score displays)
9
+ ============================================================================ */
10
+
11
+ .kpi-card {
12
+ background: var(--card-bg-strong, var(--block-background-fill));
13
+ border: 2px solid var(--accent-strong, #4f46e5);
14
+ padding: 24px;
15
+ border-radius: var(--slide-radius-lg, 12px);
16
+ text-align: center;
17
+ max-width: 600px;
18
+ margin: auto;
19
+ color: var(--text-main, #1f2937);
20
+ box-shadow: var(--shadow-drop, 0 4px 6px -1px rgba(0,0,0,0.08));
21
+ min-height: 200px;
22
+ }
23
+
24
+ .kpi-card-body {
25
+ display: flex;
26
+ flex-wrap: wrap;
27
+ justify-content: space-around;
28
+ align-items: flex-end;
29
+ margin-top: 24px;
30
+ }
31
+
32
+ .kpi-metric-box {
33
+ min-width: 150px;
34
+ margin: 10px;
35
+ }
36
+
37
+ .kpi-label {
38
+ font-size: 1rem;
39
+ color: var(--text-muted, #6b7280);
40
+ margin: 0;
41
+ }
42
+
43
+ .kpi-score {
44
+ font-size: 3rem;
45
+ font-weight: 700;
46
+ margin: 0;
47
+ line-height: 1.1;
48
+ color: var(--accent-strong, #4f46e5);
49
+ }
50
+
51
+ .kpi-subtext-muted {
52
+ font-size: 1.2rem;
53
+ font-weight: 500;
54
+ color: var(--text-muted, #6b7280);
55
+ }
56
+
57
+ /* KPI Card Variants */
58
+ .kpi-card--neutral {
59
+ border-color: var(--card-border-subtle, #e5e7eb);
60
+ }
61
+
62
+ .kpi-card--subtle-accent {
63
+ border-color: var(--accent-strong, #4f46e5);
64
+ }
65
+
66
+ .kpi-score--muted {
67
+ color: var(--text-muted, #6b7280);
68
+ }
69
+
70
+ /* ============================================================================
71
+ Leaderboard Tables
72
+ ============================================================================ */
73
+
74
+ .leaderboard-html-table {
75
+ width: 100%;
76
+ border-collapse: collapse;
77
+ text-align: left;
78
+ font-size: 1rem;
79
+ color: var(--text-main, #1f2937);
80
+ min-height: 300px;
81
+ }
82
+
83
+ .leaderboard-html-table thead {
84
+ background: var(--block-background-fill, #f9fafb);
85
+ }
86
+
87
+ .leaderboard-html-table th {
88
+ padding: 12px 16px;
89
+ font-size: 0.9rem;
90
+ color: var(--text-muted, #6b7280);
91
+ font-weight: 500;
92
+ }
93
+
94
+ .leaderboard-html-table tbody tr {
95
+ border-bottom: 1px solid var(--card-border-subtle, #e5e7eb);
96
+ }
97
+
98
+ .leaderboard-html-table td {
99
+ padding: 12px 16px;
100
+ }
101
+
102
+ .leaderboard-html-table tbody tr:hover {
103
+ background: var(--block-background-fill, #f9fafb);
104
+ }
105
+
106
+ /* User row highlighting */
107
+ .user-row-highlight {
108
+ background: color-mix(in srgb, var(--accent-strong, #4f46e5) 10%, transparent) !important;
109
+ font-weight: 600;
110
+ }
111
+
112
+ .user-row-highlight td {
113
+ color: var(--accent-strong, #4f46e5);
114
+ }
115
+
116
+ /* ============================================================================
117
+ Buttons (Consistent variants)
118
+ ============================================================================ */
119
+
120
+ /* Primary buttons - main progression actions */
121
+ .btn-primary {
122
+ background: var(--accent-strong, #4f46e5);
123
+ color: white;
124
+ border: none;
125
+ padding: 12px 24px;
126
+ border-radius: 8px;
127
+ font-weight: 600;
128
+ cursor: pointer;
129
+ transition: background 0.2s;
130
+ }
131
+
132
+ .btn-primary:hover {
133
+ background: color-mix(in srgb, var(--accent-strong, #4f46e5) 90%, black);
134
+ }
135
+
136
+ /* Secondary buttons - back, cancel */
137
+ .btn-secondary {
138
+ background: var(--block-background-fill, #f9fafb);
139
+ color: var(--text-main, #1f2937);
140
+ border: 2px solid var(--card-border-subtle, #e5e7eb);
141
+ padding: 12px 24px;
142
+ border-radius: 8px;
143
+ font-weight: 600;
144
+ cursor: pointer;
145
+ transition: all 0.2s;
146
+ }
147
+
148
+ .btn-secondary:hover {
149
+ border-color: var(--accent-strong, #4f46e5);
150
+ }
151
+
152
+ /* Neutral buttons - force sync, utility actions */
153
+ .btn-neutral {
154
+ background: var(--block-background-fill, #f9fafb);
155
+ color: var(--text-muted, #6b7280);
156
+ border: 1px solid var(--card-border-subtle, #e5e7eb);
157
+ padding: 10px 20px;
158
+ border-radius: 8px;
159
+ font-weight: 500;
160
+ cursor: pointer;
161
+ transition: all 0.2s;
162
+ }
163
+
164
+ .btn-neutral:hover {
165
+ background: var(--card-bg-strong, var(--block-background-fill));
166
+ }
167
+
168
+ /* Success buttons - certificate, completion */
169
+ .btn-success {
170
+ background: #10b981;
171
+ color: white;
172
+ border: none;
173
+ padding: 12px 24px;
174
+ border-radius: 8px;
175
+ font-weight: 600;
176
+ cursor: pointer;
177
+ transition: background 0.2s;
178
+ }
179
+
180
+ .btn-success:hover {
181
+ background: #059669;
182
+ }
183
+
184
+ /* Info buttons - educational expansions, help */
185
+ .btn-info {
186
+ background: #3b82f6;
187
+ color: white;
188
+ border: none;
189
+ padding: 10px 20px;
190
+ border-radius: 8px;
191
+ font-weight: 500;
192
+ cursor: pointer;
193
+ transition: background 0.2s;
194
+ }
195
+
196
+ .btn-info:hover {
197
+ background: #2563eb;
198
+ }
199
+
200
+ /* ============================================================================
201
+ Moral Compass Widget
202
+ ============================================================================ */
203
+
204
+ .moral-compass-widget {
205
+ background: var(--block-background-fill, #f9fafb);
206
+ padding: 16px;
207
+ border-radius: 8px;
208
+ border: 2px solid var(--accent-strong, #4f46e5);
209
+ margin: 16px 0;
210
+ }
211
+
212
+ .moral-compass-widget h3 {
213
+ margin-top: 0;
214
+ color: var(--text-main, #1f2937);
215
+ }
216
+
217
+ .moral-compass-metrics {
218
+ display: flex;
219
+ justify-content: space-around;
220
+ flex-wrap: wrap;
221
+ }
222
+
223
+ .moral-compass-metric {
224
+ text-align: center;
225
+ margin: 10px;
226
+ }
227
+
228
+ .moral-compass-metric-label {
229
+ font-size: 0.9rem;
230
+ color: var(--text-muted, #6b7280);
231
+ }
232
+
233
+ .moral-compass-metric-value {
234
+ font-size: 2rem;
235
+ font-weight: bold;
236
+ color: var(--accent-strong, #4f46e5);
237
+ }
238
+
239
+ .moral-compass-metric-status {
240
+ font-size: 0.8rem;
241
+ color: var(--text-muted, #6b7280);
242
+ }
243
+
244
+ /* ============================================================================
245
+ Alert Panels (Error/Warning/Success messages)
246
+ ============================================================================ */
247
+
248
+ .alert-panel {
249
+ padding: 16px;
250
+ border-radius: 8px;
251
+ margin: 16px 0;
252
+ border-left: 4px solid;
253
+ }
254
+
255
+ .alert-panel--error {
256
+ background: #fef2f2;
257
+ border-color: #ef4444;
258
+ color: #991b1b;
259
+ }
260
+
261
+ .alert-panel--warning {
262
+ background: #fffbeb;
263
+ border-color: #f59e0b;
264
+ color: #92400e;
265
+ }
266
+
267
+ .alert-panel--success {
268
+ background: #f0fdf4;
269
+ border-color: #10b981;
270
+ color: #065f46;
271
+ }
272
+
273
+ .alert-panel--info {
274
+ background: #eff6ff;
275
+ border-color: #3b82f6;
276
+ color: #1e40af;
277
+ }
278
+
279
+ /* ============================================================================
280
+ Educational Content Expansions
281
+ ============================================================================ */
282
+
283
+ .educational-expansion {
284
+ background: var(--block-background-fill, #f9fafb);
285
+ padding: 20px;
286
+ border-radius: 8px;
287
+ border: 1px solid var(--card-border-subtle, #e5e7eb);
288
+ margin: 16px 0;
289
+ }
290
+
291
+ .educational-expansion h4 {
292
+ margin-top: 0;
293
+ color: var(--accent-strong, #4f46e5);
294
+ }
295
+
296
+ .educational-expansion-content {
297
+ color: var(--text-main, #1f2937);
298
+ line-height: 1.6;
299
+ }
300
+
301
+ /* ============================================================================
302
+ Dark Mode Support
303
+ ============================================================================ */
304
+
305
+ @media (prefers-color-scheme: dark) {
306
+ .kpi-card {
307
+ background: color-mix(in srgb, var(--block-background-fill) 85%, #000 15%);
308
+ }
309
+
310
+ .leaderboard-html-table thead {
311
+ background: color-mix(in srgb, var(--block-background-fill) 75%, #000 25%);
312
+ }
313
+
314
+ .btn-secondary {
315
+ background: color-mix(in srgb, var(--block-background-fill) 90%, #fff 10%);
316
+ }
317
+
318
+ .btn-neutral {
319
+ background: color-mix(in srgb, var(--block-background-fill) 85%, #fff 15%);
320
+ }
321
+ }
322
+
323
+ /* ============================================================================
324
+ Responsive Design
325
+ ============================================================================ */
326
+
327
+ @media (max-width: 768px) {
328
+ .kpi-card {
329
+ padding: 16px;
330
+ min-height: 150px;
331
+ }
332
+
333
+ .kpi-score {
334
+ font-size: 2rem;
335
+ }
336
+
337
+ .leaderboard-html-table {
338
+ font-size: 0.9rem;
339
+ }
340
+
341
+ .leaderboard-html-table th,
342
+ .leaderboard-html-table td {
343
+ padding: 8px 12px;
344
+ }
345
+
346
+ .moral-compass-metric-value {
347
+ font-size: 1.5rem;
348
+ }
349
+ }