J94 commited on
Commit
7c88df9
·
verified ·
1 Parent(s): d0c4b5b

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tailwindcss filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,12 +1,19 @@
1
  ---
2
- title: Colpali Vespa
3
- emoji: 🐨
4
- colorFrom: pink
5
- colorTo: indigo
 
6
  sdk: gradio
7
- sdk_version: 5.9.1
8
- app_file: app.py
9
  pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
1
  ---
2
+ title: ColPali 🤝 Vespa - Visual Retrieval
3
+ short_description: Visual Retrieval with ColPali and Vespa
4
+ emoji: 👀
5
+ colorFrom: purple
6
+ colorTo: blue
7
  sdk: gradio
8
+ sdk_version: 4.44.0
9
+ app_file: main.py
10
  pinned: false
11
+ license: apache-2.0
12
+ suggested_hardware: t4-small
13
+ models:
14
+ - vidore/colpaligemma-3b-pt-448-base
15
+ - vidore/colpali-v1.2
16
+ preload_from_hub:
17
+ - vidore/colpaligemma-3b-pt-448-base config.json,model-00001-of-00002.safetensors,model-00002-of-00002.safetensors,model.safetensors.index.json,preprocessor_config.json,special_tokens_map.json,tokenizer.json,tokenizer_config.json 12c59eb7e23bc4c26876f7be7c17760d5d3a1ffa
18
+ - vidore/colpali-v1.2 adapter_config.json,adapter_model.safetensors,preprocessor_config.json,special_tokens_map.json,tokenizer.json,tokenizer_config.json 9912ce6f8a462d8cf2269f5606eabbd2784e764f
19
+ ---
backend/__init__.py ADDED
File without changes
backend/cache.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import OrderedDict
2
+
3
+
4
+ # Initialize LRU Cache
5
+ class LRUCache:
6
+ def __init__(self, max_size=20):
7
+ self.max_size = max_size
8
+ self.cache = OrderedDict()
9
+
10
+ def get(self, key):
11
+ if key in self.cache:
12
+ self.cache.move_to_end(key)
13
+ return self.cache[key]
14
+ return None
15
+
16
+ def set(self, key, value):
17
+ if key in self.cache:
18
+ self.cache.move_to_end(key)
19
+ else:
20
+ if len(self.cache) >= self.max_size:
21
+ self.cache.popitem(last=False)
22
+ self.cache[key] = value
23
+
24
+ def delete(self, key):
25
+ if key in self.cache:
26
+ del self.cache[key]
backend/colpali.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from PIL import Image
3
+ import numpy as np
4
+ from typing import Generator, Tuple, List, Union, Dict
5
+ from pathlib import Path
6
+ import base64
7
+ from io import BytesIO
8
+ import re
9
+ import io
10
+ import matplotlib.cm as cm
11
+
12
+ from colpali_engine.models import ColPali, ColPaliProcessor
13
+ from colpali_engine.utils.torch_utils import get_torch_device
14
+ from vidore_benchmark.interpretability.torch_utils import (
15
+ normalize_similarity_map_per_query_token,
16
+ )
17
+ from functools import lru_cache
18
+ import logging
19
+
20
+
21
+ class SimMapGenerator:
22
+ """
23
+ Generates similarity maps based on query embeddings and image patches using the ColPali model.
24
+ """
25
+
26
+ colormap = cm.get_cmap("viridis") # Preload colormap for efficiency
27
+
28
+ def __init__(
29
+ self,
30
+ logger: logging.Logger,
31
+ model_name: str = "vidore/colpali-v1.2",
32
+ n_patch: int = 32,
33
+ ):
34
+ """
35
+ Initializes the SimMapGenerator class with a specified model and patch dimension.
36
+
37
+ Args:
38
+ model_name (str): The model name for loading the ColPali model.
39
+ n_patch (int): The number of patches per dimension.
40
+ """
41
+ self.model_name = model_name
42
+ self.n_patch = n_patch
43
+ self.device = get_torch_device("auto")
44
+ self.logger = logger
45
+ self.logger.info(f"Using device: {self.device}")
46
+ self.model, self.processor = self.load_model()
47
+
48
+ def load_model(self) -> Tuple[ColPali, ColPaliProcessor]:
49
+ """
50
+ Loads the ColPali model and processor.
51
+
52
+ Returns:
53
+ Tuple[ColPali, ColPaliProcessor]: Loaded model and processor.
54
+ """
55
+ model = ColPali.from_pretrained(
56
+ self.model_name,
57
+ torch_dtype=torch.bfloat16, # Note that the embeddings created during feed were float32 -> binarized, yet setting this seem to produce the most similar results both locally (mps) and HF (Cuda)
58
+ device_map=self.device,
59
+ ).eval()
60
+
61
+ processor = ColPaliProcessor.from_pretrained(self.model_name)
62
+ return model, processor
63
+
64
+ def gen_similarity_maps(
65
+ self,
66
+ query: str,
67
+ query_embs: torch.Tensor,
68
+ token_idx_map: Dict[int, str],
69
+ images: List[Union[Path, str]],
70
+ vespa_sim_maps: List[Dict],
71
+ ) -> Generator[Tuple[int, str, str], None, None]:
72
+ """
73
+ Generates similarity maps for the provided images and query, and returns base64-encoded blended images.
74
+
75
+ Args:
76
+ query (str): The query string.
77
+ query_embs (torch.Tensor): Query embeddings tensor.
78
+ token_idx_map (dict): Mapping from indices to tokens.
79
+ images (List[Union[Path, str]]): List of image paths or base64-encoded strings.
80
+ vespa_sim_maps (List[Dict]): List of Vespa similarity maps.
81
+
82
+ Yields:
83
+ Tuple[int, str, str]: A tuple containing the image index, selected token, and base64-encoded image.
84
+ """
85
+ processed_images, original_images, original_sizes = [], [], []
86
+ for img in images:
87
+ img_pil = self._load_image(img)
88
+ original_images.append(img_pil.copy())
89
+ original_sizes.append(img_pil.size)
90
+ processed_images.append(img_pil)
91
+
92
+ vespa_sim_map_tensor = self._prepare_similarity_map_tensor(
93
+ query_embs, vespa_sim_maps
94
+ )
95
+ similarity_map_normalized = normalize_similarity_map_per_query_token(
96
+ vespa_sim_map_tensor
97
+ )
98
+
99
+ for idx, img in enumerate(original_images):
100
+ for token_idx, token in token_idx_map.items():
101
+ if self.should_filter_token(token):
102
+ continue
103
+
104
+ sim_map = similarity_map_normalized[idx, token_idx, :, :]
105
+ blended_img_base64 = self._blend_image(
106
+ img, sim_map, original_sizes[idx]
107
+ )
108
+ yield idx, token, token_idx, blended_img_base64
109
+
110
+ def _load_image(self, img: Union[Path, str]) -> Image:
111
+ """
112
+ Loads an image from a file path or a base64-encoded string.
113
+
114
+ Args:
115
+ img (Union[Path, str]): The image to load.
116
+
117
+ Returns:
118
+ Image: The loaded PIL image.
119
+ """
120
+ try:
121
+ if isinstance(img, Path):
122
+ return Image.open(img).convert("RGB")
123
+ elif isinstance(img, str):
124
+ return Image.open(BytesIO(base64.b64decode(img))).convert("RGB")
125
+ except Exception as e:
126
+ raise ValueError(f"Failed to load image: {e}")
127
+
128
+ def _prepare_similarity_map_tensor(
129
+ self, query_embs: torch.Tensor, vespa_sim_maps: List[Dict]
130
+ ) -> torch.Tensor:
131
+ """
132
+ Prepares a similarity map tensor from Vespa similarity maps.
133
+
134
+ Args:
135
+ query_embs (torch.Tensor): Query embeddings tensor.
136
+ vespa_sim_maps (List[Dict]): List of Vespa similarity maps.
137
+
138
+ Returns:
139
+ torch.Tensor: The prepared similarity map tensor.
140
+ """
141
+ vespa_sim_map_tensor = torch.zeros(
142
+ (len(vespa_sim_maps), query_embs.size(1), self.n_patch, self.n_patch)
143
+ )
144
+ for idx, vespa_sim_map in enumerate(vespa_sim_maps):
145
+ for cell in vespa_sim_map["quantized"]["cells"]:
146
+ patch = int(cell["address"]["patch"])
147
+ query_token = int(cell["address"]["querytoken"])
148
+ value = cell["value"]
149
+ if hasattr(self.processor, "image_seq_length"):
150
+ image_seq_length = self.processor.image_seq_length
151
+ else:
152
+ image_seq_length = 1024
153
+
154
+ if patch >= image_seq_length:
155
+ continue
156
+ vespa_sim_map_tensor[
157
+ idx,
158
+ query_token,
159
+ patch // self.n_patch,
160
+ patch % self.n_patch,
161
+ ] = value
162
+ return vespa_sim_map_tensor
163
+
164
+ def _blend_image(
165
+ self, img: Image, sim_map: torch.Tensor, original_size: Tuple[int, int]
166
+ ) -> str:
167
+ """
168
+ Blends an image with a similarity map and encodes it to base64.
169
+
170
+ Args:
171
+ img (Image): The original image.
172
+ sim_map (torch.Tensor): The similarity map tensor.
173
+ original_size (Tuple[int, int]): The original size of the image.
174
+
175
+ Returns:
176
+ str: The base64-encoded blended image.
177
+ """
178
+ SCALING_FACTOR = 8
179
+ sim_map_resolution = (
180
+ max(32, int(original_size[0] / SCALING_FACTOR)),
181
+ max(32, int(original_size[1] / SCALING_FACTOR)),
182
+ )
183
+
184
+ sim_map_np = sim_map.cpu().float().numpy()
185
+ sim_map_img = Image.fromarray(sim_map_np).resize(
186
+ sim_map_resolution, resample=Image.BICUBIC
187
+ )
188
+ sim_map_resized_np = np.array(sim_map_img, dtype=np.float32)
189
+ sim_map_normalized = self._normalize_sim_map(sim_map_resized_np)
190
+
191
+ heatmap = self.colormap(sim_map_normalized)
192
+ heatmap_img = Image.fromarray((heatmap * 255).astype(np.uint8)).convert("RGBA")
193
+
194
+ buffer = io.BytesIO()
195
+ heatmap_img.save(buffer, format="PNG")
196
+ return base64.b64encode(buffer.getvalue()).decode("utf-8")
197
+
198
+ @staticmethod
199
+ def _normalize_sim_map(sim_map: np.ndarray) -> np.ndarray:
200
+ """
201
+ Normalizes a similarity map to range [0, 1].
202
+
203
+ Args:
204
+ sim_map (np.ndarray): The similarity map.
205
+
206
+ Returns:
207
+ np.ndarray: The normalized similarity map.
208
+ """
209
+ sim_map_min, sim_map_max = sim_map.min(), sim_map.max()
210
+ if sim_map_max - sim_map_min > 1e-6:
211
+ return (sim_map - sim_map_min) / (sim_map_max - sim_map_min)
212
+ return np.zeros_like(sim_map)
213
+
214
+ @staticmethod
215
+ def should_filter_token(token: str) -> bool:
216
+ """
217
+ Determines if a token should be filtered out based on predefined patterns.
218
+
219
+ The function filters out tokens that:
220
+
221
+ - Start with '<' (e.g., '<bos>')
222
+ - Consist entirely of whitespace
223
+ - Are purely punctuation (excluding tokens that contain digits or start with '▁')
224
+ - Start with an underscore '_'
225
+ - Exactly match the word 'Question'
226
+ - Are exactly the single character '▁'
227
+
228
+ Output of test:
229
+ Token: '2' | False
230
+ Token: '0' | False
231
+ Token: '2' | False
232
+ Token: '3' | False
233
+ Token: '▁2' | False
234
+ Token: '▁hi' | False
235
+ Token: 'norwegian' | False
236
+ Token: 'unlisted' | False
237
+ Token: '<bos>' | True
238
+ Token: 'Question' | True
239
+ Token: ':' | True
240
+ Token: '<pad>' | True
241
+ Token: '\n' | True
242
+ Token: '▁' | True
243
+ Token: '?' | True
244
+ Token: ')' | True
245
+ Token: '%' | True
246
+ Token: '/)' | True
247
+
248
+
249
+ Args:
250
+ token (str): The token to check.
251
+
252
+ Returns:
253
+ bool: True if the token should be filtered out, False otherwise.
254
+ """
255
+ pattern = re.compile(
256
+ r"^<.*$|^\s+$|^(?!.*\d)(?!▁)[^\w\s]+$|^_.*$|^Question$|^▁$"
257
+ )
258
+ return bool(pattern.match(token))
259
+
260
+ @lru_cache(maxsize=128)
261
+ def get_query_embeddings_and_token_map(
262
+ self, query: str
263
+ ) -> Tuple[torch.Tensor, dict]:
264
+ """
265
+ Retrieves query embeddings and a token index map.
266
+
267
+ Args:
268
+ query (str): The query string.
269
+
270
+ Returns:
271
+ Tuple[torch.Tensor, dict]: Query embeddings and token index map.
272
+ """
273
+ inputs = self.processor.process_queries([query]).to(self.model.device)
274
+ with torch.no_grad():
275
+ q_emb = self.model(**inputs).to("cpu")[0]
276
+
277
+ query_tokens = self.processor.tokenizer.tokenize(
278
+ self.processor.decode(inputs.input_ids[0])
279
+ )
280
+ idx_to_token = {idx: token for idx, token in enumerate(query_tokens)}
281
+ return q_emb, idx_to_token
backend/stopwords.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spacy
2
+ import os
3
+
4
+ # Download the model if it is not already present
5
+ if not spacy.util.is_package("en_core_web_sm"):
6
+ spacy.cli.download("en_core_web_sm")
7
+ nlp = spacy.load("en_core_web_sm")
8
+
9
+
10
+ # It would be possible to remove bolding for stopwords without removing them from the query,
11
+ # but that would require a java plugin which we didn't want to complicate this sample app with.
12
+ def filter(text):
13
+ doc = nlp(text)
14
+ tokens = [token.text for token in doc if not token.is_stop]
15
+ if len(tokens) == 0:
16
+ # if we remove all the words we don't have a query at all, so use the original
17
+ return text
18
+ return " ".join(tokens)
backend/testquery.py ADDED
@@ -0,0 +1,3013 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ token_to_idx = {
4
+ "<bos>": 0,
5
+ "Question": 1,
6
+ ":": 2,
7
+ "▁Percentage": 3,
8
+ "▁of": 4,
9
+ "▁non": 5,
10
+ "-": 6,
11
+ "fresh": 7,
12
+ "▁water": 8,
13
+ "▁as": 9,
14
+ "▁source": 10,
15
+ "?": 11,
16
+ "<pad>": 21,
17
+ "\n": 22,
18
+ }
19
+ idx_to_token = {v: k for k, v in token_to_idx.items()}
20
+ q_embs = torch.tensor(
21
+ [
22
+ [
23
+ 1.6547e-01,
24
+ -1.8838e-02,
25
+ 1.0150e-01,
26
+ -2.2643e-02,
27
+ 5.0652e-02,
28
+ 3.2039e-02,
29
+ 6.8322e-02,
30
+ 2.7134e-02,
31
+ 7.2197e-03,
32
+ -4.2341e-02,
33
+ -5.8006e-02,
34
+ -1.1389e-01,
35
+ 7.0041e-02,
36
+ -6.8021e-02,
37
+ 2.4681e-02,
38
+ 5.3306e-02,
39
+ 4.1714e-02,
40
+ 6.2021e-02,
41
+ 1.3488e-01,
42
+ 3.4943e-02,
43
+ 3.8032e-02,
44
+ -3.2724e-02,
45
+ -1.2960e-01,
46
+ 1.1453e-02,
47
+ -2.6477e-02,
48
+ 3.5219e-02,
49
+ -7.6606e-02,
50
+ 2.2387e-01,
51
+ -3.4888e-02,
52
+ -4.0333e-02,
53
+ 1.4128e-01,
54
+ 4.2248e-02,
55
+ -1.2664e-01,
56
+ -7.8376e-02,
57
+ -2.0356e-02,
58
+ 4.2198e-02,
59
+ -7.0776e-02,
60
+ 1.3965e-02,
61
+ 4.2442e-03,
62
+ 7.1987e-02,
63
+ 8.5172e-04,
64
+ -6.4878e-02,
65
+ -1.8954e-01,
66
+ -8.6171e-02,
67
+ 9.1983e-02,
68
+ -9.3358e-02,
69
+ 2.2704e-01,
70
+ 1.3102e-02,
71
+ 6.5327e-02,
72
+ 2.4815e-02,
73
+ -1.4533e-01,
74
+ 5.8823e-02,
75
+ -6.1434e-02,
76
+ 5.2004e-02,
77
+ -8.4065e-02,
78
+ 1.6298e-01,
79
+ 8.1965e-02,
80
+ 2.6553e-02,
81
+ -1.2377e-01,
82
+ -5.3495e-02,
83
+ -3.4537e-02,
84
+ 5.1438e-02,
85
+ 8.2665e-03,
86
+ 7.9407e-02,
87
+ 5.8799e-02,
88
+ -3.5538e-02,
89
+ 1.9870e-01,
90
+ 6.2459e-02,
91
+ 1.6154e-01,
92
+ 7.2921e-02,
93
+ -9.7275e-02,
94
+ 3.0933e-02,
95
+ -1.0579e-02,
96
+ -1.4484e-01,
97
+ -4.8761e-02,
98
+ -5.3119e-02,
99
+ 6.2644e-02,
100
+ 2.2985e-02,
101
+ -2.1209e-01,
102
+ 9.0963e-02,
103
+ -2.6955e-02,
104
+ -7.7520e-02,
105
+ 1.2072e-01,
106
+ -1.9626e-02,
107
+ 5.8813e-02,
108
+ -1.2730e-01,
109
+ 1.5610e-01,
110
+ -1.6914e-01,
111
+ 6.5033e-02,
112
+ 8.5765e-02,
113
+ 1.2701e-01,
114
+ 6.5633e-02,
115
+ -1.0309e-01,
116
+ -8.0259e-02,
117
+ 4.5913e-02,
118
+ -3.3277e-02,
119
+ 1.9227e-01,
120
+ -2.3351e-02,
121
+ -8.0545e-02,
122
+ -9.8760e-03,
123
+ -4.1836e-02,
124
+ 1.2041e-01,
125
+ 8.1419e-02,
126
+ 1.4848e-01,
127
+ 7.2537e-02,
128
+ -4.7115e-03,
129
+ 4.1489e-02,
130
+ 4.3031e-02,
131
+ -1.3515e-01,
132
+ 1.0383e-01,
133
+ -9.4411e-02,
134
+ 2.8965e-02,
135
+ 1.9185e-01,
136
+ -1.4600e-02,
137
+ -8.0910e-02,
138
+ -2.9022e-02,
139
+ -4.4347e-02,
140
+ -8.8980e-03,
141
+ 2.8737e-03,
142
+ 4.2124e-02,
143
+ -1.6609e-02,
144
+ 4.2994e-02,
145
+ -7.2814e-02,
146
+ -2.9573e-02,
147
+ -1.2666e-01,
148
+ -4.3703e-02,
149
+ -7.2094e-02,
150
+ -2.7486e-02,
151
+ ],
152
+ [
153
+ -9.8454e-02,
154
+ -1.2954e-01,
155
+ 6.5702e-02,
156
+ -9.0006e-03,
157
+ -1.0934e-01,
158
+ 3.2155e-02,
159
+ -1.1444e-01,
160
+ -1.0309e-01,
161
+ 5.7024e-02,
162
+ 1.0124e-01,
163
+ 2.0721e-02,
164
+ -5.2608e-03,
165
+ 6.9916e-02,
166
+ 1.8036e-02,
167
+ 3.1653e-02,
168
+ 2.1923e-02,
169
+ -9.2523e-02,
170
+ -1.8215e-02,
171
+ 1.2974e-01,
172
+ -2.9632e-02,
173
+ -1.3854e-01,
174
+ 2.5710e-02,
175
+ -1.1727e-03,
176
+ 1.0245e-01,
177
+ -2.0731e-01,
178
+ 4.3669e-02,
179
+ -7.0196e-02,
180
+ -1.6697e-01,
181
+ 6.6050e-02,
182
+ 9.9776e-02,
183
+ -1.2227e-01,
184
+ 9.4000e-02,
185
+ 1.1945e-01,
186
+ 2.4611e-02,
187
+ -1.4073e-01,
188
+ 9.3476e-02,
189
+ 2.1170e-01,
190
+ -7.4522e-02,
191
+ -5.3362e-02,
192
+ -4.1198e-03,
193
+ 8.3880e-02,
194
+ 2.6590e-02,
195
+ -4.8489e-02,
196
+ -3.1279e-02,
197
+ -9.3401e-03,
198
+ -1.5945e-01,
199
+ -6.6368e-02,
200
+ 7.5715e-02,
201
+ 5.6884e-02,
202
+ 1.2861e-01,
203
+ 1.0073e-02,
204
+ -1.7185e-02,
205
+ 1.1545e-01,
206
+ 2.8725e-02,
207
+ -9.4969e-02,
208
+ -4.5517e-02,
209
+ 3.1253e-02,
210
+ 2.1135e-02,
211
+ -1.4505e-02,
212
+ 9.0893e-02,
213
+ -6.2680e-02,
214
+ -7.2855e-02,
215
+ -1.1275e-01,
216
+ -1.8433e-01,
217
+ 1.4693e-01,
218
+ 4.0366e-02,
219
+ 6.6879e-02,
220
+ -8.5653e-03,
221
+ 1.0663e-01,
222
+ -1.2342e-01,
223
+ 2.5350e-01,
224
+ -3.2227e-02,
225
+ 9.9404e-02,
226
+ 3.7340e-02,
227
+ 4.5462e-04,
228
+ -2.3015e-01,
229
+ 4.9006e-02,
230
+ 1.0079e-01,
231
+ -4.7179e-02,
232
+ 6.7642e-02,
233
+ -1.0833e-01,
234
+ 1.0030e-01,
235
+ 1.2838e-01,
236
+ -9.2911e-03,
237
+ 1.2342e-01,
238
+ -4.8455e-02,
239
+ 5.3904e-03,
240
+ 4.5178e-02,
241
+ 3.8961e-02,
242
+ 1.3383e-01,
243
+ -1.2236e-02,
244
+ 8.2026e-03,
245
+ 4.3735e-03,
246
+ -7.1725e-02,
247
+ 6.4360e-02,
248
+ 1.0004e-01,
249
+ 6.6840e-02,
250
+ 6.5649e-02,
251
+ -1.3978e-02,
252
+ 1.2810e-01,
253
+ 4.4325e-03,
254
+ 1.1136e-01,
255
+ -1.7329e-01,
256
+ -3.4472e-02,
257
+ -1.4066e-01,
258
+ 1.5641e-02,
259
+ -3.3600e-02,
260
+ -7.6192e-02,
261
+ 5.3085e-02,
262
+ -7.2859e-03,
263
+ 2.8798e-02,
264
+ -3.3748e-02,
265
+ -7.7591e-02,
266
+ -4.0927e-02,
267
+ -3.6577e-02,
268
+ 1.4012e-02,
269
+ -8.1780e-02,
270
+ -4.6315e-03,
271
+ -1.6508e-02,
272
+ -2.4506e-02,
273
+ 1.5122e-01,
274
+ 7.8270e-02,
275
+ 8.6502e-02,
276
+ -2.4651e-02,
277
+ -1.0286e-01,
278
+ -1.2171e-02,
279
+ -7.9000e-02,
280
+ 1.1161e-01,
281
+ ],
282
+ [
283
+ 6.4320e-02,
284
+ -2.6815e-02,
285
+ 1.2390e-01,
286
+ 8.6642e-02,
287
+ 5.3320e-02,
288
+ 9.1074e-02,
289
+ -8.9753e-02,
290
+ 1.6141e-02,
291
+ -5.0281e-02,
292
+ 1.0177e-01,
293
+ 4.5343e-02,
294
+ 9.1281e-02,
295
+ -8.2592e-03,
296
+ -1.4100e-02,
297
+ -4.7048e-02,
298
+ -8.6034e-02,
299
+ -1.1608e-01,
300
+ -8.4754e-02,
301
+ 1.0302e-01,
302
+ -1.2210e-02,
303
+ 8.5147e-02,
304
+ 1.3103e-01,
305
+ -3.3592e-03,
306
+ -1.4328e-02,
307
+ -4.6128e-03,
308
+ 2.1401e-02,
309
+ -1.7464e-01,
310
+ -3.9111e-02,
311
+ 2.0886e-02,
312
+ 8.9284e-02,
313
+ 1.3262e-01,
314
+ 7.0918e-02,
315
+ -1.3693e-02,
316
+ -9.7673e-02,
317
+ -5.3411e-02,
318
+ -6.7563e-02,
319
+ 2.3017e-02,
320
+ -3.4614e-02,
321
+ 3.6464e-02,
322
+ -4.6408e-02,
323
+ 1.4866e-01,
324
+ -2.1191e-01,
325
+ -6.4368e-02,
326
+ -3.0555e-02,
327
+ 7.2177e-02,
328
+ 2.4685e-02,
329
+ 8.6115e-02,
330
+ -5.8688e-02,
331
+ -2.8230e-03,
332
+ 1.1166e-01,
333
+ 1.8380e-01,
334
+ 3.6462e-02,
335
+ -1.4943e-02,
336
+ -1.4276e-01,
337
+ 1.0795e-01,
338
+ 1.9204e-02,
339
+ -6.5320e-02,
340
+ 1.0561e-01,
341
+ -1.3642e-01,
342
+ -4.3444e-02,
343
+ -6.4725e-02,
344
+ -1.3094e-01,
345
+ 1.9447e-02,
346
+ -1.3199e-01,
347
+ 8.7880e-02,
348
+ 5.8078e-02,
349
+ -2.5612e-02,
350
+ 7.7620e-02,
351
+ -2.5044e-02,
352
+ 1.0772e-02,
353
+ 6.5417e-02,
354
+ 8.2137e-02,
355
+ -2.8482e-02,
356
+ 5.5003e-02,
357
+ -1.1163e-01,
358
+ 1.7200e-03,
359
+ -1.0106e-01,
360
+ -1.8413e-02,
361
+ 1.2838e-01,
362
+ -1.2991e-01,
363
+ -1.8546e-02,
364
+ 1.0517e-01,
365
+ 1.0279e-01,
366
+ -8.1887e-02,
367
+ 1.0885e-01,
368
+ -1.0635e-01,
369
+ 1.2035e-01,
370
+ 1.1769e-01,
371
+ -2.8768e-02,
372
+ -3.3413e-02,
373
+ 1.3779e-01,
374
+ 1.4403e-02,
375
+ 2.3429e-02,
376
+ -1.2761e-01,
377
+ 7.2160e-02,
378
+ -1.0512e-01,
379
+ -1.7202e-02,
380
+ 5.3549e-02,
381
+ 5.5205e-02,
382
+ 9.2863e-02,
383
+ -2.3728e-02,
384
+ -5.1368e-02,
385
+ -3.7719e-02,
386
+ -4.7308e-02,
387
+ -2.2489e-02,
388
+ 4.5195e-02,
389
+ 1.0398e-01,
390
+ -2.2197e-02,
391
+ -1.7208e-01,
392
+ 7.4649e-02,
393
+ -7.7925e-02,
394
+ -6.4237e-02,
395
+ 2.8195e-02,
396
+ 2.2692e-01,
397
+ -9.7749e-02,
398
+ 2.4283e-01,
399
+ -4.0124e-02,
400
+ 1.8797e-02,
401
+ -6.1516e-02,
402
+ 6.5331e-03,
403
+ 1.3717e-01,
404
+ 9.9761e-02,
405
+ 5.4705e-02,
406
+ 3.5325e-02,
407
+ 1.9071e-01,
408
+ -6.1137e-02,
409
+ 1.6656e-01,
410
+ 3.4067e-02,
411
+ ],
412
+ [
413
+ 1.0896e-01,
414
+ -9.4366e-03,
415
+ 1.2956e-01,
416
+ 7.8127e-02,
417
+ 5.5422e-02,
418
+ 3.9155e-02,
419
+ -5.8379e-03,
420
+ -4.4257e-02,
421
+ -7.5182e-02,
422
+ 1.0452e-01,
423
+ 4.4595e-02,
424
+ 2.0972e-02,
425
+ 1.1071e-01,
426
+ 7.4710e-02,
427
+ -7.5500e-02,
428
+ -5.3393e-02,
429
+ -1.5478e-02,
430
+ -5.4455e-03,
431
+ 5.6779e-02,
432
+ -6.3919e-02,
433
+ 5.1792e-02,
434
+ 1.2070e-01,
435
+ -5.3707e-02,
436
+ 4.5715e-02,
437
+ -9.3062e-02,
438
+ 3.0224e-02,
439
+ -1.5892e-01,
440
+ -2.1702e-02,
441
+ 1.7942e-03,
442
+ 1.4574e-01,
443
+ 2.0721e-01,
444
+ -2.8224e-02,
445
+ -5.8104e-02,
446
+ -1.1645e-01,
447
+ -1.1515e-01,
448
+ -1.5202e-01,
449
+ -3.9751e-02,
450
+ 6.0342e-02,
451
+ 7.8182e-02,
452
+ -2.1132e-02,
453
+ 9.6468e-02,
454
+ -5.3148e-02,
455
+ -3.0343e-02,
456
+ 7.9363e-02,
457
+ 1.0752e-01,
458
+ 3.1086e-02,
459
+ 1.9322e-02,
460
+ -1.1134e-01,
461
+ 1.6342e-02,
462
+ 3.0358e-02,
463
+ 1.8543e-01,
464
+ 5.5353e-02,
465
+ -8.6656e-02,
466
+ -1.5650e-01,
467
+ 1.2087e-01,
468
+ -3.7852e-02,
469
+ -6.9116e-02,
470
+ 5.9981e-03,
471
+ 1.9205e-02,
472
+ -1.0314e-01,
473
+ -6.8082e-02,
474
+ -1.3078e-01,
475
+ 3.8448e-02,
476
+ -9.2233e-02,
477
+ 1.0965e-01,
478
+ 6.6332e-02,
479
+ -2.5805e-02,
480
+ 1.2299e-01,
481
+ 2.8629e-02,
482
+ -4.4949e-02,
483
+ 4.5560e-02,
484
+ 1.0507e-01,
485
+ -1.0271e-01,
486
+ 1.6237e-02,
487
+ -1.4555e-01,
488
+ -4.5335e-02,
489
+ -1.3477e-01,
490
+ 1.0230e-02,
491
+ 1.2380e-01,
492
+ -1.0681e-01,
493
+ 1.4412e-02,
494
+ 1.2396e-01,
495
+ 8.5290e-02,
496
+ -2.5138e-02,
497
+ 1.0191e-01,
498
+ -1.3413e-01,
499
+ 8.5871e-02,
500
+ 1.3389e-01,
501
+ -3.6357e-02,
502
+ -3.9740e-02,
503
+ 2.1128e-01,
504
+ 1.0263e-02,
505
+ 2.5547e-02,
506
+ -7.0139e-02,
507
+ 1.0178e-01,
508
+ -1.2729e-01,
509
+ -1.0717e-01,
510
+ -4.9394e-02,
511
+ 7.7645e-02,
512
+ 7.4589e-02,
513
+ -8.2835e-02,
514
+ -4.2227e-02,
515
+ -3.6417e-02,
516
+ -2.2900e-02,
517
+ -2.1010e-02,
518
+ 2.7898e-02,
519
+ 2.7314e-02,
520
+ 2.2172e-02,
521
+ -7.1122e-02,
522
+ 5.1570e-02,
523
+ 2.1860e-02,
524
+ -3.5103e-03,
525
+ -5.4524e-02,
526
+ 1.7485e-01,
527
+ -8.3810e-02,
528
+ 2.3868e-01,
529
+ 5.9468e-02,
530
+ -2.6706e-02,
531
+ -2.6617e-02,
532
+ 3.3851e-02,
533
+ 6.3651e-02,
534
+ 1.0611e-01,
535
+ 9.6252e-02,
536
+ -6.8701e-02,
537
+ 1.8108e-01,
538
+ -1.0178e-01,
539
+ 1.6935e-01,
540
+ 5.9301e-02,
541
+ ],
542
+ [
543
+ 1.3364e-01,
544
+ 6.6797e-02,
545
+ 1.0182e-01,
546
+ 6.1569e-02,
547
+ -1.4169e-04,
548
+ 1.1567e-01,
549
+ -3.1255e-03,
550
+ -8.9336e-02,
551
+ 5.3206e-03,
552
+ 1.7179e-01,
553
+ 1.3974e-01,
554
+ -6.5797e-02,
555
+ 1.2566e-01,
556
+ 6.1290e-02,
557
+ -1.3671e-01,
558
+ -1.1113e-01,
559
+ 1.6596e-01,
560
+ 6.2694e-02,
561
+ -1.6573e-02,
562
+ -1.8648e-02,
563
+ 2.1086e-02,
564
+ 9.6320e-03,
565
+ -1.0017e-01,
566
+ 2.3561e-02,
567
+ -6.2797e-02,
568
+ 2.1202e-02,
569
+ -1.3333e-01,
570
+ 1.4728e-01,
571
+ 1.9824e-03,
572
+ 1.6832e-01,
573
+ 1.7138e-01,
574
+ 4.6578e-02,
575
+ -1.2989e-01,
576
+ 3.2167e-02,
577
+ -7.7753e-02,
578
+ -1.1548e-01,
579
+ 4.3367e-02,
580
+ -4.5640e-02,
581
+ 4.3583e-02,
582
+ -8.9393e-03,
583
+ 5.3231e-02,
584
+ -4.8284e-02,
585
+ 3.1841e-04,
586
+ 6.6374e-02,
587
+ 7.7363e-02,
588
+ 4.0000e-02,
589
+ 1.5414e-02,
590
+ -9.6156e-02,
591
+ 1.1389e-01,
592
+ 7.1118e-02,
593
+ 3.0042e-02,
594
+ 5.9752e-02,
595
+ -6.4565e-02,
596
+ -1.2914e-01,
597
+ 1.1048e-01,
598
+ 1.2409e-02,
599
+ -9.9385e-02,
600
+ 1.8671e-02,
601
+ 2.1383e-02,
602
+ 3.7012e-03,
603
+ -1.1497e-01,
604
+ -6.8653e-02,
605
+ 3.0582e-02,
606
+ -1.1567e-01,
607
+ 1.4165e-01,
608
+ 3.7493e-02,
609
+ -6.0779e-02,
610
+ 1.0989e-01,
611
+ 8.6001e-02,
612
+ -5.6139e-02,
613
+ 2.0710e-02,
614
+ 9.8577e-02,
615
+ -9.9427e-02,
616
+ 5.8372e-02,
617
+ -1.3443e-01,
618
+ -1.3021e-02,
619
+ -1.3802e-01,
620
+ 7.6053e-02,
621
+ 1.2181e-01,
622
+ -8.7719e-02,
623
+ 1.0967e-02,
624
+ 1.3160e-01,
625
+ 4.7032e-02,
626
+ -6.3351e-02,
627
+ 7.1883e-02,
628
+ -9.7565e-02,
629
+ 1.4424e-01,
630
+ 1.2353e-01,
631
+ -6.1527e-02,
632
+ 2.4263e-02,
633
+ 2.9356e-01,
634
+ 6.2813e-02,
635
+ -4.5265e-03,
636
+ -1.0213e-01,
637
+ 1.4227e-02,
638
+ -7.9267e-02,
639
+ -1.0845e-01,
640
+ -2.0014e-02,
641
+ 2.8542e-02,
642
+ 9.7207e-02,
643
+ -2.5234e-02,
644
+ -7.3668e-02,
645
+ -3.0084e-02,
646
+ -1.2958e-02,
647
+ -3.9597e-02,
648
+ -7.2243e-02,
649
+ 5.3054e-02,
650
+ 3.1470e-03,
651
+ -1.9800e-02,
652
+ 1.3476e-01,
653
+ -1.6873e-02,
654
+ 5.2286e-02,
655
+ 2.0254e-02,
656
+ 1.0554e-01,
657
+ -3.0395e-02,
658
+ 8.6349e-02,
659
+ 7.6580e-02,
660
+ -3.0139e-02,
661
+ -4.8131e-02,
662
+ -4.5770e-02,
663
+ 1.5154e-01,
664
+ 1.1276e-01,
665
+ 5.7244e-02,
666
+ 8.0574e-02,
667
+ 1.5610e-01,
668
+ -1.5523e-01,
669
+ 1.0428e-01,
670
+ 5.7947e-02,
671
+ ],
672
+ [
673
+ 7.0440e-02,
674
+ 1.4300e-01,
675
+ 6.0559e-02,
676
+ 1.9177e-02,
677
+ -9.0313e-02,
678
+ 1.7104e-01,
679
+ -5.1137e-02,
680
+ -1.0229e-01,
681
+ -2.8831e-02,
682
+ 1.5385e-01,
683
+ -8.4017e-03,
684
+ -4.9185e-03,
685
+ 1.0820e-01,
686
+ 1.0022e-01,
687
+ -1.9284e-01,
688
+ -2.7293e-02,
689
+ 4.9526e-02,
690
+ 5.5152e-02,
691
+ -6.7003e-02,
692
+ -7.0313e-03,
693
+ -3.3208e-02,
694
+ 1.3815e-02,
695
+ -6.0694e-02,
696
+ 6.2041e-02,
697
+ -3.2288e-02,
698
+ 1.1629e-01,
699
+ -7.5270e-02,
700
+ 2.1824e-01,
701
+ -2.5215e-03,
702
+ 1.8179e-01,
703
+ 1.5514e-01,
704
+ 1.0494e-01,
705
+ -1.2390e-01,
706
+ -1.2241e-03,
707
+ 6.1079e-04,
708
+ -5.8730e-02,
709
+ 6.1313e-02,
710
+ -7.8853e-02,
711
+ 6.0292e-02,
712
+ -9.1497e-04,
713
+ 7.9087e-02,
714
+ -1.8246e-02,
715
+ 5.0215e-03,
716
+ 3.5083e-02,
717
+ 5.9616e-02,
718
+ 5.9520e-02,
719
+ 6.0224e-02,
720
+ -1.3079e-01,
721
+ 1.6500e-01,
722
+ 4.4308e-03,
723
+ 4.2712e-02,
724
+ 5.5916e-02,
725
+ -5.4616e-02,
726
+ -8.5617e-02,
727
+ 1.1235e-01,
728
+ 6.5911e-03,
729
+ -6.1463e-02,
730
+ 3.7832e-02,
731
+ 3.4189e-02,
732
+ -1.1295e-02,
733
+ -8.0972e-02,
734
+ -1.0051e-02,
735
+ -2.6856e-02,
736
+ -7.9570e-02,
737
+ 1.2776e-01,
738
+ 6.5826e-02,
739
+ -3.1759e-02,
740
+ 9.6016e-02,
741
+ 6.7249e-02,
742
+ -4.5115e-02,
743
+ 6.3695e-03,
744
+ 1.2092e-01,
745
+ -1.3821e-01,
746
+ -9.7066e-02,
747
+ -1.5063e-02,
748
+ 2.4618e-02,
749
+ -1.9589e-01,
750
+ 5.8625e-02,
751
+ 1.7886e-01,
752
+ -6.3740e-02,
753
+ -1.7241e-02,
754
+ 7.3394e-02,
755
+ 5.8903e-02,
756
+ -1.6557e-02,
757
+ -1.9226e-02,
758
+ -1.0912e-01,
759
+ 8.7902e-02,
760
+ 6.4426e-02,
761
+ 4.8019e-02,
762
+ 8.1223e-02,
763
+ 2.9888e-01,
764
+ 8.7458e-02,
765
+ 4.4167e-02,
766
+ -1.3228e-01,
767
+ 5.9629e-02,
768
+ -1.0696e-01,
769
+ -1.4102e-01,
770
+ -5.2509e-02,
771
+ -1.9981e-02,
772
+ 1.6788e-01,
773
+ 9.7499e-02,
774
+ -5.4125e-02,
775
+ -8.2383e-02,
776
+ -6.3908e-02,
777
+ -6.8830e-02,
778
+ -1.2622e-01,
779
+ 3.1651e-02,
780
+ 4.4592e-02,
781
+ -1.3325e-02,
782
+ 1.1260e-01,
783
+ -3.9567e-02,
784
+ 6.9631e-03,
785
+ 1.4943e-01,
786
+ 8.6930e-02,
787
+ -3.6171e-03,
788
+ -5.6886e-02,
789
+ -8.3102e-03,
790
+ -2.6001e-02,
791
+ -1.5187e-02,
792
+ -1.8835e-02,
793
+ 2.3583e-02,
794
+ 9.5520e-02,
795
+ -3.4944e-02,
796
+ 4.5537e-02,
797
+ 6.1444e-02,
798
+ -1.7165e-01,
799
+ 1.0230e-01,
800
+ 2.0319e-02,
801
+ ],
802
+ [
803
+ 7.7065e-02,
804
+ 1.4466e-01,
805
+ 1.0796e-01,
806
+ 9.7362e-03,
807
+ -9.3062e-02,
808
+ 2.0065e-01,
809
+ -9.3982e-03,
810
+ -1.2871e-01,
811
+ -4.6724e-02,
812
+ 1.6573e-01,
813
+ -1.7444e-02,
814
+ -3.1211e-02,
815
+ 9.7404e-02,
816
+ 9.6222e-02,
817
+ -1.4772e-01,
818
+ -5.6838e-02,
819
+ 9.6276e-02,
820
+ 7.7819e-02,
821
+ -1.1884e-01,
822
+ -4.5898e-02,
823
+ -7.3665e-02,
824
+ 2.1005e-02,
825
+ -2.8032e-02,
826
+ 7.0900e-02,
827
+ -7.7625e-02,
828
+ 7.0269e-02,
829
+ -7.0747e-02,
830
+ 2.5605e-01,
831
+ 1.9427e-04,
832
+ 1.4614e-01,
833
+ 1.6723e-01,
834
+ 1.0016e-01,
835
+ -7.8961e-02,
836
+ 1.3307e-02,
837
+ -1.1207e-02,
838
+ -7.1492e-02,
839
+ 7.5561e-02,
840
+ -1.3290e-02,
841
+ 4.4527e-02,
842
+ -2.4224e-02,
843
+ 8.7846e-02,
844
+ 4.7492e-02,
845
+ -7.0398e-02,
846
+ -2.6663e-02,
847
+ 4.9730e-02,
848
+ 6.6743e-02,
849
+ 3.1060e-02,
850
+ -7.8352e-02,
851
+ 1.0100e-01,
852
+ 6.4963e-02,
853
+ 1.6746e-02,
854
+ 3.0324e-02,
855
+ -2.6583e-02,
856
+ -7.8028e-02,
857
+ 7.0180e-02,
858
+ 3.8924e-02,
859
+ -8.5861e-02,
860
+ -3.3792e-02,
861
+ 6.1542e-02,
862
+ 1.6180e-02,
863
+ -7.7865e-02,
864
+ 3.9551e-02,
865
+ 2.9772e-02,
866
+ -7.0824e-02,
867
+ 1.5235e-01,
868
+ 4.8718e-02,
869
+ 1.5973e-03,
870
+ 8.7719e-02,
871
+ 7.7414e-02,
872
+ -6.4385e-02,
873
+ -6.4330e-02,
874
+ 1.3965e-01,
875
+ -1.6355e-01,
876
+ -6.5261e-02,
877
+ -6.2693e-02,
878
+ 4.9435e-02,
879
+ -1.5245e-01,
880
+ 6.6557e-02,
881
+ 1.5213e-01,
882
+ -8.2073e-02,
883
+ 1.4664e-02,
884
+ 8.4507e-02,
885
+ 3.0684e-02,
886
+ -8.7932e-02,
887
+ 1.9927e-02,
888
+ -7.1788e-02,
889
+ 9.4965e-02,
890
+ 3.9220e-02,
891
+ 4.5944e-02,
892
+ 8.7249e-02,
893
+ 3.3315e-01,
894
+ 5.9872e-02,
895
+ 2.6362e-02,
896
+ -1.7888e-01,
897
+ -1.6042e-02,
898
+ -9.4593e-02,
899
+ -1.7915e-01,
900
+ -2.9888e-02,
901
+ -4.3776e-02,
902
+ 1.1388e-01,
903
+ 2.3778e-02,
904
+ -4.0233e-02,
905
+ -4.7893e-02,
906
+ -4.4371e-02,
907
+ -2.7491e-02,
908
+ -9.6716e-02,
909
+ -2.0120e-02,
910
+ 5.6864e-02,
911
+ 1.8953e-02,
912
+ 1.2741e-01,
913
+ -5.3045e-02,
914
+ 3.2240e-02,
915
+ 1.4479e-01,
916
+ 9.5315e-02,
917
+ -2.7717e-02,
918
+ -5.7349e-02,
919
+ 3.3824e-03,
920
+ -3.5642e-02,
921
+ -1.6905e-02,
922
+ -4.5765e-02,
923
+ 1.1481e-02,
924
+ 4.2545e-02,
925
+ 1.2632e-02,
926
+ 4.5401e-02,
927
+ 7.7769e-02,
928
+ -1.7107e-01,
929
+ 7.7265e-02,
930
+ 1.3922e-02,
931
+ ],
932
+ [
933
+ -2.5666e-02,
934
+ 9.8268e-02,
935
+ 2.1571e-01,
936
+ 6.8094e-02,
937
+ -1.0440e-01,
938
+ 1.4299e-01,
939
+ 6.5751e-02,
940
+ -5.0693e-02,
941
+ -5.3796e-02,
942
+ 1.5239e-01,
943
+ 2.9566e-02,
944
+ -7.9492e-02,
945
+ 9.3274e-02,
946
+ 7.6112e-02,
947
+ -1.8187e-02,
948
+ -1.1190e-01,
949
+ 9.7962e-02,
950
+ -2.8204e-02,
951
+ -8.1216e-02,
952
+ -5.5618e-02,
953
+ -6.2378e-02,
954
+ 4.4238e-02,
955
+ -1.6572e-02,
956
+ -3.4035e-02,
957
+ -8.8068e-02,
958
+ 1.4164e-02,
959
+ -2.6908e-02,
960
+ 1.9650e-01,
961
+ 6.8845e-03,
962
+ 8.7550e-02,
963
+ 1.7410e-01,
964
+ 1.0088e-01,
965
+ -1.1340e-02,
966
+ 3.2057e-04,
967
+ -5.5130e-02,
968
+ -2.5234e-02,
969
+ 8.2460e-02,
970
+ -6.0768e-02,
971
+ 1.2448e-01,
972
+ 6.8736e-02,
973
+ 4.6176e-02,
974
+ 1.0866e-01,
975
+ 4.9560e-02,
976
+ -5.8322e-02,
977
+ 4.4106e-02,
978
+ 2.0739e-02,
979
+ -9.0032e-02,
980
+ -5.8815e-02,
981
+ 7.8127e-03,
982
+ 1.7999e-01,
983
+ 1.2519e-01,
984
+ 7.1377e-02,
985
+ 9.3219e-02,
986
+ -1.3311e-01,
987
+ 6.0305e-02,
988
+ 5.9400e-02,
989
+ -1.6119e-01,
990
+ 5.4173e-02,
991
+ -5.8663e-02,
992
+ -4.8149e-02,
993
+ 1.6295e-02,
994
+ -6.9787e-02,
995
+ 5.7512e-03,
996
+ -8.9745e-03,
997
+ 1.3492e-01,
998
+ 3.0659e-02,
999
+ -6.8611e-02,
1000
+ 2.1200e-02,
1001
+ 8.5522e-02,
1002
+ -4.3482e-02,
1003
+ -8.4601e-02,
1004
+ 1.4191e-01,
1005
+ -1.5514e-01,
1006
+ -5.8989e-03,
1007
+ -4.5591e-02,
1008
+ 6.4905e-02,
1009
+ -1.3198e-01,
1010
+ 1.3764e-01,
1011
+ 9.5549e-02,
1012
+ -9.4689e-02,
1013
+ -1.9705e-02,
1014
+ 2.1147e-01,
1015
+ -9.8519e-03,
1016
+ -7.7839e-02,
1017
+ 6.1447e-02,
1018
+ -6.4708e-02,
1019
+ 3.1579e-03,
1020
+ 7.6588e-02,
1021
+ -1.2452e-01,
1022
+ -6.1076e-02,
1023
+ 2.5150e-01,
1024
+ 2.3101e-02,
1025
+ -1.3632e-02,
1026
+ -8.5695e-02,
1027
+ -8.5841e-02,
1028
+ -1.3152e-01,
1029
+ -1.5294e-01,
1030
+ -4.4509e-03,
1031
+ 8.6619e-02,
1032
+ -1.0974e-02,
1033
+ -3.9592e-02,
1034
+ -3.0472e-02,
1035
+ -1.4011e-01,
1036
+ 8.6485e-03,
1037
+ -1.3633e-02,
1038
+ -1.7940e-02,
1039
+ 3.3016e-02,
1040
+ -5.7245e-02,
1041
+ 1.0200e-01,
1042
+ 1.2807e-01,
1043
+ 1.5249e-02,
1044
+ -1.2197e-02,
1045
+ -1.6867e-02,
1046
+ 3.4516e-02,
1047
+ -9.0908e-02,
1048
+ -2.6167e-02,
1049
+ 2.2975e-01,
1050
+ 4.2693e-02,
1051
+ 2.5415e-03,
1052
+ 7.1921e-03,
1053
+ 1.2855e-01,
1054
+ 5.5747e-03,
1055
+ -2.7843e-02,
1056
+ 2.4283e-02,
1057
+ -1.3484e-02,
1058
+ -1.5948e-01,
1059
+ 2.1127e-02,
1060
+ 3.8017e-02,
1061
+ ],
1062
+ [
1063
+ 5.8410e-02,
1064
+ 3.7501e-02,
1065
+ 1.7189e-01,
1066
+ 4.5894e-02,
1067
+ -6.7739e-02,
1068
+ 1.1421e-01,
1069
+ 4.6982e-02,
1070
+ -1.2963e-01,
1071
+ -4.2804e-02,
1072
+ 1.2137e-01,
1073
+ 1.0761e-01,
1074
+ -7.2615e-02,
1075
+ 1.1811e-01,
1076
+ 1.1291e-01,
1077
+ -1.6041e-01,
1078
+ -6.6820e-02,
1079
+ 1.9071e-01,
1080
+ -3.6201e-02,
1081
+ -1.0659e-01,
1082
+ -3.3226e-02,
1083
+ -2.6535e-02,
1084
+ 7.8536e-02,
1085
+ -3.2975e-02,
1086
+ 1.3015e-02,
1087
+ -1.0903e-01,
1088
+ -1.2502e-02,
1089
+ -7.0142e-02,
1090
+ 1.6872e-01,
1091
+ -3.7569e-02,
1092
+ 2.2090e-01,
1093
+ 1.5620e-01,
1094
+ 9.5620e-02,
1095
+ -7.9306e-02,
1096
+ 7.8558e-02,
1097
+ -6.6709e-02,
1098
+ -6.6446e-02,
1099
+ 3.1198e-02,
1100
+ -4.8808e-02,
1101
+ 6.7798e-02,
1102
+ -4.9524e-02,
1103
+ 1.2496e-01,
1104
+ 3.1661e-02,
1105
+ 4.0690e-02,
1106
+ -1.1341e-02,
1107
+ 2.9852e-03,
1108
+ 3.6166e-02,
1109
+ -1.1723e-01,
1110
+ -8.2746e-02,
1111
+ 1.2874e-01,
1112
+ 1.0894e-01,
1113
+ 7.6219e-02,
1114
+ 5.5564e-02,
1115
+ 4.3571e-02,
1116
+ -1.4745e-01,
1117
+ 5.1090e-02,
1118
+ -6.8233e-04,
1119
+ -1.7107e-01,
1120
+ -2.5612e-02,
1121
+ 1.9185e-02,
1122
+ 3.4466e-03,
1123
+ -2.5362e-02,
1124
+ -3.4807e-02,
1125
+ 8.1003e-02,
1126
+ -2.1582e-02,
1127
+ 1.1581e-01,
1128
+ -5.5891e-04,
1129
+ 4.2455e-02,
1130
+ 6.3277e-02,
1131
+ 7.9868e-02,
1132
+ -2.0369e-02,
1133
+ -7.6229e-02,
1134
+ 1.2525e-01,
1135
+ -1.4795e-01,
1136
+ 5.8798e-03,
1137
+ -2.8584e-02,
1138
+ -3.2508e-04,
1139
+ -5.5666e-02,
1140
+ 7.1217e-02,
1141
+ 1.3122e-01,
1142
+ -1.8671e-02,
1143
+ -6.6402e-02,
1144
+ 8.1978e-02,
1145
+ -5.5194e-02,
1146
+ -1.7259e-02,
1147
+ 7.1373e-02,
1148
+ -2.4721e-02,
1149
+ 1.0176e-01,
1150
+ 1.7447e-02,
1151
+ -8.2872e-02,
1152
+ 4.7707e-02,
1153
+ 2.9338e-01,
1154
+ 3.0481e-02,
1155
+ 1.3437e-02,
1156
+ -1.1888e-01,
1157
+ -6.1243e-02,
1158
+ -8.7197e-02,
1159
+ -1.1085e-01,
1160
+ 3.2847e-03,
1161
+ 1.0611e-02,
1162
+ 1.2908e-01,
1163
+ -2.9064e-02,
1164
+ 4.5522e-03,
1165
+ -7.8045e-02,
1166
+ -1.4551e-02,
1167
+ -3.3958e-02,
1168
+ -1.3626e-01,
1169
+ 1.0859e-01,
1170
+ -7.3438e-03,
1171
+ 9.5942e-02,
1172
+ 1.4176e-01,
1173
+ 7.3675e-02,
1174
+ -2.3085e-03,
1175
+ -1.1689e-02,
1176
+ 6.1119e-02,
1177
+ -9.3085e-02,
1178
+ -1.9003e-03,
1179
+ 1.9013e-01,
1180
+ -2.5321e-02,
1181
+ -7.0616e-02,
1182
+ -3.7145e-02,
1183
+ 1.2449e-01,
1184
+ 5.3496e-02,
1185
+ 6.4021e-02,
1186
+ 2.2668e-02,
1187
+ 7.3348e-02,
1188
+ -1.7717e-01,
1189
+ 8.7206e-02,
1190
+ -9.0490e-03,
1191
+ ],
1192
+ [
1193
+ 1.0227e-01,
1194
+ 4.4796e-02,
1195
+ 4.0916e-02,
1196
+ 1.2557e-01,
1197
+ 6.2699e-02,
1198
+ 8.8194e-02,
1199
+ 1.0194e-02,
1200
+ -1.8886e-01,
1201
+ -2.6260e-02,
1202
+ 1.8107e-01,
1203
+ 1.1127e-01,
1204
+ -4.9960e-03,
1205
+ 8.2291e-02,
1206
+ 6.0932e-02,
1207
+ -1.3735e-01,
1208
+ -8.1350e-02,
1209
+ 1.2948e-01,
1210
+ 1.0171e-01,
1211
+ -7.5950e-02,
1212
+ 4.7169e-02,
1213
+ 3.5525e-03,
1214
+ -1.4790e-02,
1215
+ -3.9063e-02,
1216
+ -1.9754e-02,
1217
+ -4.6450e-02,
1218
+ 4.9071e-03,
1219
+ -3.5914e-02,
1220
+ 1.7644e-01,
1221
+ 4.1860e-02,
1222
+ 1.0857e-01,
1223
+ 8.5257e-02,
1224
+ 1.0368e-01,
1225
+ -1.7223e-01,
1226
+ 4.6316e-02,
1227
+ -3.9480e-02,
1228
+ -9.5817e-02,
1229
+ 1.5301e-03,
1230
+ -4.5519e-02,
1231
+ 1.2380e-01,
1232
+ -3.0402e-02,
1233
+ 4.9047e-02,
1234
+ 3.4955e-02,
1235
+ -1.3018e-02,
1236
+ 7.4193e-02,
1237
+ -3.0426e-02,
1238
+ 1.8414e-02,
1239
+ -1.6606e-02,
1240
+ -1.6301e-01,
1241
+ 1.5784e-01,
1242
+ 1.3759e-01,
1243
+ 3.1906e-02,
1244
+ 2.9389e-02,
1245
+ -9.4501e-02,
1246
+ -1.9930e-01,
1247
+ 1.3336e-01,
1248
+ 3.0685e-02,
1249
+ -7.3809e-02,
1250
+ 6.2165e-02,
1251
+ 7.3050e-02,
1252
+ 3.2870e-02,
1253
+ -1.2857e-01,
1254
+ -7.5806e-04,
1255
+ 1.3985e-01,
1256
+ -5.5108e-02,
1257
+ 9.2220e-02,
1258
+ 7.2490e-02,
1259
+ -2.6251e-02,
1260
+ 3.4508e-02,
1261
+ -3.1215e-02,
1262
+ -8.1111e-02,
1263
+ 1.4316e-02,
1264
+ 1.2390e-01,
1265
+ -9.6291e-03,
1266
+ 6.4214e-02,
1267
+ -9.6013e-02,
1268
+ 7.5809e-02,
1269
+ -1.5899e-01,
1270
+ 8.6961e-02,
1271
+ 1.5239e-03,
1272
+ -5.6370e-02,
1273
+ -4.3367e-02,
1274
+ 1.5813e-02,
1275
+ 9.7189e-04,
1276
+ -5.2946e-02,
1277
+ 4.5950e-02,
1278
+ -1.0028e-01,
1279
+ 1.1108e-01,
1280
+ 9.0491e-03,
1281
+ -3.5540e-02,
1282
+ -1.2020e-02,
1283
+ 2.2980e-01,
1284
+ 2.7125e-02,
1285
+ -2.4191e-02,
1286
+ -1.1363e-01,
1287
+ -1.0109e-01,
1288
+ -1.4781e-01,
1289
+ -4.7656e-02,
1290
+ 3.9481e-02,
1291
+ 5.4198e-02,
1292
+ 8.2908e-02,
1293
+ 1.4034e-02,
1294
+ -1.8492e-02,
1295
+ -6.8612e-02,
1296
+ -4.8741e-02,
1297
+ 1.1223e-02,
1298
+ 3.9220e-02,
1299
+ 5.4551e-04,
1300
+ 6.5554e-02,
1301
+ 4.3087e-02,
1302
+ 1.4678e-01,
1303
+ -4.4496e-02,
1304
+ 6.2379e-02,
1305
+ 4.7876e-02,
1306
+ 7.0156e-02,
1307
+ -6.4684e-02,
1308
+ 6.1076e-02,
1309
+ 1.4685e-01,
1310
+ -6.3639e-02,
1311
+ -8.7487e-02,
1312
+ -1.3756e-02,
1313
+ 1.2724e-01,
1314
+ 1.7404e-01,
1315
+ 6.7980e-02,
1316
+ 6.8036e-02,
1317
+ 1.9786e-01,
1318
+ -9.2910e-02,
1319
+ 1.9158e-01,
1320
+ 4.2686e-02,
1321
+ ],
1322
+ [
1323
+ 4.7626e-02,
1324
+ 9.3338e-02,
1325
+ 9.8020e-02,
1326
+ 9.2408e-02,
1327
+ 1.8267e-02,
1328
+ 1.9572e-02,
1329
+ 1.1056e-01,
1330
+ -1.2639e-01,
1331
+ -3.1999e-02,
1332
+ 1.3731e-01,
1333
+ 1.4826e-01,
1334
+ -6.7136e-02,
1335
+ 8.9095e-02,
1336
+ 1.9683e-01,
1337
+ -6.5839e-02,
1338
+ -1.0708e-01,
1339
+ 1.4414e-01,
1340
+ 4.7389e-02,
1341
+ -7.9921e-02,
1342
+ 2.7793e-02,
1343
+ 9.7240e-02,
1344
+ 3.8000e-03,
1345
+ -4.2359e-02,
1346
+ 5.9870e-02,
1347
+ -8.2957e-02,
1348
+ -2.1429e-02,
1349
+ -5.1302e-02,
1350
+ 7.6202e-02,
1351
+ 3.5426e-02,
1352
+ 8.1509e-02,
1353
+ 1.0849e-01,
1354
+ 2.1857e-01,
1355
+ -9.1445e-02,
1356
+ 1.1869e-01,
1357
+ -1.1225e-02,
1358
+ -1.6530e-01,
1359
+ 2.9216e-02,
1360
+ -7.2792e-02,
1361
+ 2.9544e-02,
1362
+ 5.3102e-02,
1363
+ 2.7746e-02,
1364
+ 1.8145e-01,
1365
+ 4.4050e-02,
1366
+ 8.2549e-02,
1367
+ 2.3603e-02,
1368
+ 1.1494e-02,
1369
+ -9.1516e-02,
1370
+ -6.2333e-02,
1371
+ 1.0970e-01,
1372
+ 1.4544e-01,
1373
+ -2.3549e-02,
1374
+ 3.8348e-02,
1375
+ -8.8504e-03,
1376
+ -1.6986e-01,
1377
+ 5.3215e-02,
1378
+ 4.0085e-02,
1379
+ -1.4667e-01,
1380
+ 8.6047e-02,
1381
+ 4.7751e-02,
1382
+ -9.0753e-03,
1383
+ -8.0938e-02,
1384
+ 7.1735e-02,
1385
+ 1.1711e-01,
1386
+ -4.2525e-02,
1387
+ 9.4244e-02,
1388
+ 6.2260e-02,
1389
+ -8.4798e-02,
1390
+ -6.2766e-02,
1391
+ 6.1025e-02,
1392
+ -7.7480e-02,
1393
+ -5.3010e-02,
1394
+ 1.0182e-01,
1395
+ -7.1081e-03,
1396
+ 1.4029e-01,
1397
+ -8.2788e-02,
1398
+ 3.0182e-02,
1399
+ -1.4279e-01,
1400
+ 4.7882e-02,
1401
+ 3.2736e-02,
1402
+ 1.0058e-02,
1403
+ -1.2673e-02,
1404
+ 3.2723e-02,
1405
+ 5.7034e-02,
1406
+ -2.0343e-02,
1407
+ -6.3037e-03,
1408
+ 6.8914e-03,
1409
+ 2.8643e-02,
1410
+ 8.2739e-02,
1411
+ -2.5124e-02,
1412
+ -7.0611e-02,
1413
+ 2.7142e-01,
1414
+ -1.6370e-02,
1415
+ 1.1240e-02,
1416
+ -1.5776e-01,
1417
+ -8.8755e-02,
1418
+ -1.2389e-01,
1419
+ -2.5785e-02,
1420
+ -3.1258e-02,
1421
+ 2.2023e-02,
1422
+ 8.5497e-02,
1423
+ 6.8360e-02,
1424
+ 9.6662e-03,
1425
+ -1.2843e-01,
1426
+ -7.4122e-02,
1427
+ 7.1105e-02,
1428
+ 5.8334e-02,
1429
+ -1.8609e-02,
1430
+ 7.2775e-02,
1431
+ -1.1696e-03,
1432
+ 1.3165e-01,
1433
+ -4.7326e-02,
1434
+ 6.8737e-02,
1435
+ -2.3211e-02,
1436
+ 5.5425e-02,
1437
+ -6.8077e-02,
1438
+ -7.3743e-04,
1439
+ 1.6215e-01,
1440
+ -4.9965e-02,
1441
+ -6.8493e-02,
1442
+ -6.8358e-03,
1443
+ 9.9518e-02,
1444
+ 1.5627e-01,
1445
+ 1.3100e-01,
1446
+ 9.0073e-03,
1447
+ 1.8023e-01,
1448
+ -2.7992e-02,
1449
+ 1.3087e-01,
1450
+ 7.5672e-02,
1451
+ ],
1452
+ [
1453
+ 9.0390e-03,
1454
+ 1.3137e-01,
1455
+ 4.6586e-02,
1456
+ 1.1836e-01,
1457
+ 1.1406e-01,
1458
+ -7.3655e-02,
1459
+ -1.4340e-02,
1460
+ -1.4285e-01,
1461
+ 2.0426e-02,
1462
+ 1.3532e-01,
1463
+ 1.3353e-01,
1464
+ -4.7242e-02,
1465
+ 2.6449e-02,
1466
+ 2.0707e-02,
1467
+ -7.5049e-02,
1468
+ -4.4098e-02,
1469
+ 1.4599e-01,
1470
+ 1.1299e-01,
1471
+ -2.2097e-02,
1472
+ 8.4679e-02,
1473
+ 5.8946e-02,
1474
+ 2.1213e-02,
1475
+ -5.8343e-03,
1476
+ -4.5902e-02,
1477
+ -6.1018e-02,
1478
+ 4.4368e-02,
1479
+ -5.7242e-02,
1480
+ 1.4734e-01,
1481
+ -2.6191e-02,
1482
+ 8.6116e-02,
1483
+ 4.6004e-02,
1484
+ 9.4532e-02,
1485
+ -1.7424e-01,
1486
+ 1.3697e-01,
1487
+ 4.6544e-02,
1488
+ -1.1718e-01,
1489
+ 8.2567e-02,
1490
+ -3.1494e-02,
1491
+ 6.2073e-02,
1492
+ -4.6328e-02,
1493
+ 1.0782e-02,
1494
+ 6.6071e-02,
1495
+ 9.6953e-03,
1496
+ 2.0415e-02,
1497
+ -7.0939e-02,
1498
+ -3.1883e-02,
1499
+ -4.9926e-02,
1500
+ -1.2694e-01,
1501
+ 1.6351e-01,
1502
+ 1.1670e-01,
1503
+ 3.8149e-02,
1504
+ 7.2657e-02,
1505
+ -1.3930e-01,
1506
+ -1.6682e-01,
1507
+ 1.2471e-01,
1508
+ 1.9094e-02,
1509
+ -6.1618e-02,
1510
+ 5.8628e-02,
1511
+ 1.8804e-03,
1512
+ -9.8104e-03,
1513
+ -1.8998e-01,
1514
+ 1.4179e-02,
1515
+ 1.4856e-01,
1516
+ -4.2194e-02,
1517
+ 5.0934e-02,
1518
+ 7.4201e-02,
1519
+ 1.5546e-02,
1520
+ 2.3192e-02,
1521
+ -5.5886e-02,
1522
+ -4.3618e-02,
1523
+ 3.6677e-02,
1524
+ 1.2750e-01,
1525
+ 2.1240e-02,
1526
+ 9.5849e-02,
1527
+ -1.2006e-01,
1528
+ 6.0424e-02,
1529
+ -1.6516e-01,
1530
+ 7.8015e-02,
1531
+ -7.6026e-02,
1532
+ -1.6711e-02,
1533
+ -3.3572e-02,
1534
+ 4.3704e-02,
1535
+ 1.2728e-02,
1536
+ -6.9630e-02,
1537
+ 4.1065e-02,
1538
+ -8.6589e-02,
1539
+ 1.1581e-01,
1540
+ 2.2666e-02,
1541
+ -5.1476e-02,
1542
+ 2.2756e-02,
1543
+ 1.9309e-01,
1544
+ 7.0417e-02,
1545
+ 8.9253e-03,
1546
+ -1.3546e-01,
1547
+ -7.7783e-02,
1548
+ -1.5058e-01,
1549
+ -4.7874e-02,
1550
+ 3.3498e-02,
1551
+ 6.4585e-02,
1552
+ 7.8633e-02,
1553
+ 1.0162e-01,
1554
+ 4.5821e-02,
1555
+ -2.8424e-02,
1556
+ 4.4524e-02,
1557
+ 4.6649e-02,
1558
+ 4.5805e-02,
1559
+ 8.9723e-02,
1560
+ 3.7597e-02,
1561
+ -6.8016e-03,
1562
+ 1.1727e-01,
1563
+ -6.6387e-02,
1564
+ 3.4958e-02,
1565
+ 4.0205e-02,
1566
+ 7.5854e-02,
1567
+ -5.1322e-02,
1568
+ 6.0060e-02,
1569
+ 1.4631e-01,
1570
+ -1.1550e-01,
1571
+ -1.5388e-01,
1572
+ 6.0975e-03,
1573
+ 1.2013e-01,
1574
+ 2.1669e-01,
1575
+ 4.8245e-02,
1576
+ 8.4634e-02,
1577
+ 1.4934e-01,
1578
+ -1.2099e-01,
1579
+ 1.6673e-01,
1580
+ 6.2539e-02,
1581
+ ],
1582
+ [
1583
+ 1.6222e-01,
1584
+ 7.5644e-03,
1585
+ 1.0667e-01,
1586
+ 8.3084e-02,
1587
+ 5.1888e-02,
1588
+ 8.7477e-02,
1589
+ -1.1550e-01,
1590
+ -4.7037e-02,
1591
+ -3.9418e-02,
1592
+ 8.0987e-02,
1593
+ 1.2812e-01,
1594
+ -6.6527e-02,
1595
+ 6.5405e-02,
1596
+ 5.2723e-03,
1597
+ -6.2940e-02,
1598
+ -5.8805e-02,
1599
+ 2.8526e-02,
1600
+ 4.0631e-02,
1601
+ 9.8513e-02,
1602
+ -2.8448e-02,
1603
+ 9.2361e-02,
1604
+ 6.9724e-02,
1605
+ -1.1792e-01,
1606
+ -1.4806e-02,
1607
+ -6.6172e-02,
1608
+ 5.9468e-02,
1609
+ -1.8325e-01,
1610
+ 3.2851e-02,
1611
+ -2.7042e-02,
1612
+ 1.7569e-01,
1613
+ 1.9061e-01,
1614
+ 4.1868e-02,
1615
+ -1.4116e-01,
1616
+ -1.6780e-02,
1617
+ -6.2881e-02,
1618
+ -9.4655e-02,
1619
+ 5.1488e-02,
1620
+ -3.6501e-03,
1621
+ 5.6754e-02,
1622
+ -6.3285e-03,
1623
+ 9.2234e-02,
1624
+ -1.0761e-01,
1625
+ -5.5392e-02,
1626
+ 7.5249e-02,
1627
+ 8.7539e-02,
1628
+ 4.3377e-02,
1629
+ 3.2473e-03,
1630
+ -1.0990e-01,
1631
+ 5.2611e-02,
1632
+ 3.4955e-02,
1633
+ 1.1722e-01,
1634
+ 4.0339e-02,
1635
+ -9.6605e-02,
1636
+ -9.3660e-02,
1637
+ 9.6494e-02,
1638
+ -1.4790e-02,
1639
+ -5.7770e-02,
1640
+ 4.3192e-02,
1641
+ -8.1741e-02,
1642
+ -4.0909e-02,
1643
+ -9.5212e-02,
1644
+ -1.2459e-01,
1645
+ 3.6241e-02,
1646
+ -1.2878e-01,
1647
+ 1.1544e-01,
1648
+ 3.6392e-03,
1649
+ -7.7986e-03,
1650
+ 8.2339e-02,
1651
+ 1.4319e-01,
1652
+ -5.8374e-02,
1653
+ 1.2483e-01,
1654
+ 5.6826e-02,
1655
+ -6.2649e-02,
1656
+ 4.5884e-02,
1657
+ -1.8261e-01,
1658
+ -9.1633e-02,
1659
+ -1.9414e-01,
1660
+ 2.8376e-02,
1661
+ 9.5654e-02,
1662
+ -1.3532e-01,
1663
+ 2.1669e-03,
1664
+ 7.9960e-02,
1665
+ 5.0917e-02,
1666
+ -5.4874e-02,
1667
+ 7.6823e-02,
1668
+ -1.3932e-01,
1669
+ 1.2431e-01,
1670
+ 1.3001e-01,
1671
+ -6.0069e-02,
1672
+ 2.6470e-02,
1673
+ 2.2378e-01,
1674
+ 2.7335e-02,
1675
+ 1.6487e-02,
1676
+ -9.6473e-02,
1677
+ 8.3110e-02,
1678
+ -8.8776e-02,
1679
+ -6.2299e-02,
1680
+ 4.0821e-02,
1681
+ 6.5279e-02,
1682
+ 8.4030e-02,
1683
+ -4.9298e-02,
1684
+ -3.7441e-02,
1685
+ -4.9233e-02,
1686
+ -1.9428e-03,
1687
+ -1.9763e-02,
1688
+ -9.1249e-02,
1689
+ 1.1593e-01,
1690
+ 6.6291e-03,
1691
+ -1.0686e-01,
1692
+ 5.9383e-02,
1693
+ -3.1088e-02,
1694
+ -1.9844e-03,
1695
+ 2.5891e-02,
1696
+ 1.3471e-01,
1697
+ -3.5624e-02,
1698
+ 1.2647e-01,
1699
+ 6.7167e-02,
1700
+ -1.8682e-02,
1701
+ -2.4806e-02,
1702
+ -1.1453e-02,
1703
+ 9.6992e-02,
1704
+ 1.2253e-01,
1705
+ 5.6707e-02,
1706
+ 1.0860e-02,
1707
+ 2.4775e-01,
1708
+ -8.8641e-02,
1709
+ 1.1375e-01,
1710
+ 6.9383e-02,
1711
+ ],
1712
+ [
1713
+ 1.4724e-01,
1714
+ 3.7511e-02,
1715
+ 1.1238e-01,
1716
+ 6.3809e-02,
1717
+ 1.8174e-03,
1718
+ 8.4854e-02,
1719
+ -7.8965e-02,
1720
+ -3.1542e-02,
1721
+ -3.7643e-02,
1722
+ 1.0136e-01,
1723
+ 1.5837e-01,
1724
+ -7.5701e-02,
1725
+ 1.0313e-01,
1726
+ 2.2795e-02,
1727
+ -1.1227e-01,
1728
+ -4.3783e-02,
1729
+ 4.6561e-02,
1730
+ 4.1225e-02,
1731
+ 7.4273e-02,
1732
+ -4.8200e-02,
1733
+ 2.7666e-02,
1734
+ 5.1417e-02,
1735
+ -1.3928e-01,
1736
+ 2.0020e-02,
1737
+ -9.3673e-02,
1738
+ 5.1440e-02,
1739
+ -2.0076e-01,
1740
+ 1.0715e-01,
1741
+ -3.0323e-02,
1742
+ 1.7241e-01,
1743
+ 1.9730e-01,
1744
+ 7.7685e-02,
1745
+ -1.5949e-01,
1746
+ 4.8495e-02,
1747
+ -8.0068e-02,
1748
+ -7.1965e-02,
1749
+ 8.7222e-02,
1750
+ -4.8383e-02,
1751
+ 7.0627e-02,
1752
+ 3.9791e-03,
1753
+ 7.0469e-02,
1754
+ -8.6697e-02,
1755
+ -3.7149e-02,
1756
+ 7.9965e-02,
1757
+ 8.8967e-02,
1758
+ 5.3325e-02,
1759
+ -5.6073e-03,
1760
+ -1.1336e-01,
1761
+ 1.0338e-01,
1762
+ 2.3824e-02,
1763
+ 6.9393e-02,
1764
+ 6.1018e-02,
1765
+ -7.2880e-02,
1766
+ -6.6328e-02,
1767
+ 8.9040e-02,
1768
+ -8.8268e-03,
1769
+ -7.5720e-02,
1770
+ 3.7544e-02,
1771
+ -8.3740e-02,
1772
+ -4.7977e-02,
1773
+ -1.2425e-01,
1774
+ -1.0304e-01,
1775
+ -3.7850e-03,
1776
+ -1.1540e-01,
1777
+ 1.0803e-01,
1778
+ 2.5964e-02,
1779
+ -1.0915e-02,
1780
+ 1.1758e-01,
1781
+ 1.6561e-01,
1782
+ -4.5171e-02,
1783
+ 1.1272e-01,
1784
+ 6.0721e-02,
1785
+ -9.6027e-02,
1786
+ 6.0908e-02,
1787
+ -1.6545e-01,
1788
+ -6.5444e-02,
1789
+ -1.6802e-01,
1790
+ 2.5850e-02,
1791
+ 1.2434e-01,
1792
+ -1.2358e-01,
1793
+ -5.2647e-04,
1794
+ 1.2329e-01,
1795
+ 6.3928e-02,
1796
+ -5.5478e-02,
1797
+ 5.4642e-02,
1798
+ -1.0372e-01,
1799
+ 1.0955e-01,
1800
+ 1.1810e-01,
1801
+ -7.7028e-02,
1802
+ 5.0302e-02,
1803
+ 2.7576e-01,
1804
+ 6.4704e-02,
1805
+ 3.7824e-03,
1806
+ -8.3016e-02,
1807
+ 6.7355e-02,
1808
+ -4.9660e-02,
1809
+ -1.2677e-01,
1810
+ 3.4498e-02,
1811
+ 2.7027e-02,
1812
+ 9.1476e-02,
1813
+ -5.1170e-02,
1814
+ -3.4786e-02,
1815
+ -4.4356e-02,
1816
+ 6.2440e-03,
1817
+ -3.5473e-02,
1818
+ -1.1076e-01,
1819
+ 1.0014e-01,
1820
+ -1.5927e-02,
1821
+ -7.9539e-02,
1822
+ 1.1542e-01,
1823
+ -3.6775e-03,
1824
+ 2.5437e-02,
1825
+ 2.1185e-02,
1826
+ 1.0472e-01,
1827
+ -4.8937e-03,
1828
+ 4.5876e-02,
1829
+ 5.3122e-02,
1830
+ -1.9815e-02,
1831
+ -1.8126e-02,
1832
+ -2.3358e-02,
1833
+ 1.2271e-01,
1834
+ 9.8860e-02,
1835
+ 4.5235e-02,
1836
+ 6.2841e-02,
1837
+ 1.6294e-01,
1838
+ -1.0909e-01,
1839
+ 7.2012e-02,
1840
+ 5.3389e-02,
1841
+ ],
1842
+ [
1843
+ 1.2605e-01,
1844
+ 5.1723e-02,
1845
+ 1.2152e-01,
1846
+ 5.8291e-02,
1847
+ -3.8019e-02,
1848
+ 8.8117e-02,
1849
+ -4.1927e-02,
1850
+ -2.3022e-02,
1851
+ -3.1158e-02,
1852
+ 1.2518e-01,
1853
+ 1.4279e-01,
1854
+ -7.1922e-02,
1855
+ 1.0867e-01,
1856
+ 3.3116e-02,
1857
+ -1.3669e-01,
1858
+ -3.2413e-02,
1859
+ 9.0301e-02,
1860
+ 5.9203e-02,
1861
+ -7.5021e-03,
1862
+ -6.9837e-02,
1863
+ -1.2463e-03,
1864
+ 1.8891e-02,
1865
+ -1.1878e-01,
1866
+ 6.1658e-02,
1867
+ -1.2649e-01,
1868
+ 4.2450e-02,
1869
+ -1.6298e-01,
1870
+ 1.5450e-01,
1871
+ -2.6732e-02,
1872
+ 1.7891e-01,
1873
+ 2.1083e-01,
1874
+ 1.0432e-01,
1875
+ -1.3396e-01,
1876
+ 6.9416e-02,
1877
+ -7.3193e-02,
1878
+ -6.8281e-02,
1879
+ 9.9454e-02,
1880
+ -4.4528e-02,
1881
+ 7.7992e-02,
1882
+ -2.6336e-03,
1883
+ 6.9279e-02,
1884
+ -3.8836e-02,
1885
+ -2.8872e-02,
1886
+ 5.6951e-02,
1887
+ 8.7154e-02,
1888
+ 6.1652e-02,
1889
+ -1.8049e-02,
1890
+ -1.2096e-01,
1891
+ 1.3820e-01,
1892
+ 5.1705e-02,
1893
+ 3.4052e-02,
1894
+ 1.1524e-01,
1895
+ -4.1216e-02,
1896
+ -8.5670e-02,
1897
+ 7.1571e-02,
1898
+ -1.6001e-02,
1899
+ -8.8427e-02,
1900
+ 3.1357e-02,
1901
+ -5.4897e-02,
1902
+ -3.1463e-02,
1903
+ -1.2070e-01,
1904
+ -5.7460e-02,
1905
+ -2.1424e-02,
1906
+ -1.2160e-01,
1907
+ 1.0026e-01,
1908
+ 4.7475e-02,
1909
+ -1.4578e-02,
1910
+ 1.3247e-01,
1911
+ 1.5272e-01,
1912
+ -2.9788e-02,
1913
+ 8.2947e-02,
1914
+ 7.9830e-02,
1915
+ -1.1615e-01,
1916
+ 7.2949e-02,
1917
+ -1.2386e-01,
1918
+ -5.3969e-02,
1919
+ -1.3578e-01,
1920
+ 4.0461e-02,
1921
+ 1.2467e-01,
1922
+ -9.4575e-02,
1923
+ 6.2182e-04,
1924
+ 1.3108e-01,
1925
+ 6.5035e-02,
1926
+ -5.7784e-02,
1927
+ 4.2513e-02,
1928
+ -7.8118e-02,
1929
+ 1.1274e-01,
1930
+ 8.9333e-02,
1931
+ -4.9991e-02,
1932
+ 7.4639e-02,
1933
+ 3.0876e-01,
1934
+ 7.3974e-02,
1935
+ -1.4039e-02,
1936
+ -7.4526e-02,
1937
+ 6.5200e-02,
1938
+ -3.6421e-02,
1939
+ -1.6730e-01,
1940
+ 4.1713e-02,
1941
+ -1.5316e-02,
1942
+ 1.0056e-01,
1943
+ -5.0370e-02,
1944
+ -4.4531e-02,
1945
+ -5.0233e-02,
1946
+ 3.2636e-02,
1947
+ -1.7609e-02,
1948
+ -1.1354e-01,
1949
+ 8.1922e-02,
1950
+ -2.0907e-02,
1951
+ -1.3264e-02,
1952
+ 1.5115e-01,
1953
+ 1.2777e-02,
1954
+ 3.8261e-02,
1955
+ 2.1829e-02,
1956
+ 5.5874e-02,
1957
+ 6.8772e-03,
1958
+ -2.7772e-02,
1959
+ 6.7979e-02,
1960
+ -3.9099e-02,
1961
+ -2.8619e-02,
1962
+ -5.6936e-02,
1963
+ 1.3366e-01,
1964
+ 9.4269e-02,
1965
+ 4.4852e-02,
1966
+ 7.6881e-02,
1967
+ 9.6787e-02,
1968
+ -1.2885e-01,
1969
+ 7.9722e-02,
1970
+ 6.4991e-02,
1971
+ ],
1972
+ [
1973
+ 1.0575e-01,
1974
+ 6.4565e-02,
1975
+ 1.2479e-01,
1976
+ 5.5616e-02,
1977
+ -5.5869e-02,
1978
+ 8.9005e-02,
1979
+ -1.9612e-02,
1980
+ -3.3032e-02,
1981
+ -3.9719e-02,
1982
+ 1.3884e-01,
1983
+ 1.2010e-01,
1984
+ -6.3224e-02,
1985
+ 1.0893e-01,
1986
+ 4.4172e-02,
1987
+ -1.4283e-01,
1988
+ -3.8972e-02,
1989
+ 1.1470e-01,
1990
+ 7.7830e-02,
1991
+ -4.6450e-02,
1992
+ -8.7246e-02,
1993
+ -5.7215e-03,
1994
+ 1.7476e-02,
1995
+ -9.0388e-02,
1996
+ 7.4607e-02,
1997
+ -1.3105e-01,
1998
+ 2.9371e-02,
1999
+ -1.2681e-01,
2000
+ 1.6014e-01,
2001
+ -1.3655e-02,
2002
+ 1.8492e-01,
2003
+ 1.9841e-01,
2004
+ 1.0942e-01,
2005
+ -1.1261e-01,
2006
+ 6.8326e-02,
2007
+ -5.1130e-02,
2008
+ -7.1296e-02,
2009
+ 9.3895e-02,
2010
+ -3.1097e-02,
2011
+ 8.7043e-02,
2012
+ -1.2087e-02,
2013
+ 7.3054e-02,
2014
+ -1.0602e-02,
2015
+ -3.6347e-02,
2016
+ 3.1324e-02,
2017
+ 7.5686e-02,
2018
+ 5.3221e-02,
2019
+ -3.1793e-02,
2020
+ -1.3818e-01,
2021
+ 1.4972e-01,
2022
+ 9.0217e-02,
2023
+ 2.6365e-02,
2024
+ 1.1733e-01,
2025
+ -2.6107e-02,
2026
+ -1.1933e-01,
2027
+ 6.5575e-02,
2028
+ -1.6907e-02,
2029
+ -1.0186e-01,
2030
+ 3.3719e-02,
2031
+ -3.4021e-02,
2032
+ -1.0597e-02,
2033
+ -1.0478e-01,
2034
+ -3.4765e-02,
2035
+ -3.0666e-03,
2036
+ -1.2035e-01,
2037
+ 1.1053e-01,
2038
+ 5.2212e-02,
2039
+ -1.8638e-02,
2040
+ 1.2226e-01,
2041
+ 1.3614e-01,
2042
+ -4.3806e-02,
2043
+ 4.7251e-02,
2044
+ 9.2144e-02,
2045
+ -1.2505e-01,
2046
+ 8.3020e-02,
2047
+ -1.1203e-01,
2048
+ -4.9654e-02,
2049
+ -1.1939e-01,
2050
+ 4.4126e-02,
2051
+ 1.1188e-01,
2052
+ -7.8910e-02,
2053
+ -4.1917e-03,
2054
+ 1.3279e-01,
2055
+ 5.0291e-02,
2056
+ -6.1770e-02,
2057
+ 5.1914e-02,
2058
+ -6.8796e-02,
2059
+ 1.2148e-01,
2060
+ 7.6837e-02,
2061
+ -4.0098e-02,
2062
+ 8.3547e-02,
2063
+ 3.0367e-01,
2064
+ 6.9357e-02,
2065
+ -2.5710e-02,
2066
+ -7.2644e-02,
2067
+ 5.2612e-02,
2068
+ -5.3110e-02,
2069
+ -1.7360e-01,
2070
+ 5.2791e-02,
2071
+ -1.6490e-02,
2072
+ 9.9930e-02,
2073
+ -4.8174e-02,
2074
+ -3.0230e-02,
2075
+ -6.0898e-02,
2076
+ 3.4064e-02,
2077
+ 8.2446e-03,
2078
+ -1.0747e-01,
2079
+ 6.9406e-02,
2080
+ -9.2400e-03,
2081
+ 2.1804e-02,
2082
+ 1.5491e-01,
2083
+ 2.0964e-02,
2084
+ 3.4691e-02,
2085
+ 3.5648e-02,
2086
+ 4.4363e-02,
2087
+ -3.2605e-03,
2088
+ -5.9988e-02,
2089
+ 9.3354e-02,
2090
+ -7.4183e-02,
2091
+ -3.5824e-02,
2092
+ -7.1163e-02,
2093
+ 1.4776e-01,
2094
+ 1.0179e-01,
2095
+ 4.1685e-02,
2096
+ 6.7698e-02,
2097
+ 8.4053e-02,
2098
+ -1.4797e-01,
2099
+ 9.9115e-02,
2100
+ 7.2061e-02,
2101
+ ],
2102
+ [
2103
+ 8.3987e-02,
2104
+ 6.9951e-02,
2105
+ 1.3728e-01,
2106
+ 6.4616e-02,
2107
+ -5.5591e-02,
2108
+ 8.8007e-02,
2109
+ -1.7926e-02,
2110
+ -4.8462e-02,
2111
+ -4.8522e-02,
2112
+ 1.5932e-01,
2113
+ 1.0766e-01,
2114
+ -4.9589e-02,
2115
+ 1.0511e-01,
2116
+ 4.4010e-02,
2117
+ -1.3653e-01,
2118
+ -4.1380e-02,
2119
+ 1.3629e-01,
2120
+ 9.2007e-02,
2121
+ -5.2961e-02,
2122
+ -7.1865e-02,
2123
+ -2.8329e-03,
2124
+ 1.3417e-02,
2125
+ -5.2565e-02,
2126
+ 6.2258e-02,
2127
+ -1.2135e-01,
2128
+ 2.7531e-02,
2129
+ -9.4902e-02,
2130
+ 1.6567e-01,
2131
+ -1.6613e-02,
2132
+ 1.8443e-01,
2133
+ 1.6290e-01,
2134
+ 1.1109e-01,
2135
+ -1.1476e-01,
2136
+ 6.5613e-02,
2137
+ -1.7283e-02,
2138
+ -8.7120e-02,
2139
+ 1.0862e-01,
2140
+ -2.5325e-02,
2141
+ 8.9025e-02,
2142
+ -1.8290e-02,
2143
+ 7.2979e-02,
2144
+ 1.1878e-04,
2145
+ -4.1007e-02,
2146
+ 1.9679e-02,
2147
+ 6.1887e-02,
2148
+ 4.0949e-02,
2149
+ -3.6016e-02,
2150
+ -1.5569e-01,
2151
+ 1.4839e-01,
2152
+ 1.1372e-01,
2153
+ 1.8360e-02,
2154
+ 9.3030e-02,
2155
+ -1.8070e-02,
2156
+ -1.5578e-01,
2157
+ 6.1180e-02,
2158
+ -1.9569e-02,
2159
+ -1.1221e-01,
2160
+ 4.0037e-02,
2161
+ -2.3712e-02,
2162
+ -1.0311e-02,
2163
+ -1.0530e-01,
2164
+ -1.2916e-02,
2165
+ 3.1546e-02,
2166
+ -1.1647e-01,
2167
+ 1.2109e-01,
2168
+ 5.8714e-02,
2169
+ -1.9454e-02,
2170
+ 9.9438e-02,
2171
+ 1.1088e-01,
2172
+ -7.4443e-02,
2173
+ 1.6456e-02,
2174
+ 8.7619e-02,
2175
+ -1.1460e-01,
2176
+ 9.7142e-02,
2177
+ -1.1083e-01,
2178
+ -5.2367e-02,
2179
+ -1.2157e-01,
2180
+ 4.6784e-02,
2181
+ 8.5968e-02,
2182
+ -7.2488e-02,
2183
+ -1.2658e-02,
2184
+ 1.4136e-01,
2185
+ 1.7934e-02,
2186
+ -6.4075e-02,
2187
+ 5.3809e-02,
2188
+ -5.8649e-02,
2189
+ 1.2410e-01,
2190
+ 6.7307e-02,
2191
+ -5.3157e-02,
2192
+ 8.5616e-02,
2193
+ 2.8266e-01,
2194
+ 6.4775e-02,
2195
+ -2.9330e-02,
2196
+ -8.7733e-02,
2197
+ 1.3974e-02,
2198
+ -7.9092e-02,
2199
+ -1.6691e-01,
2200
+ 5.9037e-02,
2201
+ 4.1784e-03,
2202
+ 9.8219e-02,
2203
+ -3.6970e-02,
2204
+ 8.6754e-03,
2205
+ -5.5234e-02,
2206
+ 3.4627e-02,
2207
+ 3.5126e-02,
2208
+ -9.5508e-02,
2209
+ 6.4539e-02,
2210
+ -9.1506e-04,
2211
+ 2.9144e-02,
2212
+ 1.3975e-01,
2213
+ 1.5472e-02,
2214
+ 3.9015e-02,
2215
+ 4.6270e-02,
2216
+ 5.4053e-02,
2217
+ -1.2525e-02,
2218
+ -6.6168e-02,
2219
+ 1.2463e-01,
2220
+ -1.0793e-01,
2221
+ -4.0659e-02,
2222
+ -8.1224e-02,
2223
+ 1.5702e-01,
2224
+ 1.2323e-01,
2225
+ 3.3473e-02,
2226
+ 6.1162e-02,
2227
+ 8.3375e-02,
2228
+ -1.7863e-01,
2229
+ 1.1536e-01,
2230
+ 6.9906e-02,
2231
+ ],
2232
+ [
2233
+ 6.2508e-02,
2234
+ 8.5713e-02,
2235
+ 1.3566e-01,
2236
+ 8.5148e-02,
2237
+ -2.2642e-02,
2238
+ 5.8889e-02,
2239
+ -3.3654e-02,
2240
+ -6.6948e-02,
2241
+ -5.1541e-02,
2242
+ 1.7082e-01,
2243
+ 1.1639e-01,
2244
+ -3.9750e-02,
2245
+ 8.5338e-02,
2246
+ 2.8182e-02,
2247
+ -1.1108e-01,
2248
+ -3.5316e-02,
2249
+ 1.4888e-01,
2250
+ 1.0210e-01,
2251
+ -3.4537e-02,
2252
+ -1.1950e-02,
2253
+ 5.0482e-03,
2254
+ 1.8051e-02,
2255
+ -2.2067e-02,
2256
+ 1.8682e-02,
2257
+ -9.7924e-02,
2258
+ 2.6326e-02,
2259
+ -7.6128e-02,
2260
+ 1.7675e-01,
2261
+ -1.6092e-02,
2262
+ 1.6355e-01,
2263
+ 1.1133e-01,
2264
+ 1.1036e-01,
2265
+ -1.4070e-01,
2266
+ 6.9723e-02,
2267
+ 2.0482e-02,
2268
+ -1.0421e-01,
2269
+ 1.2920e-01,
2270
+ -3.1272e-02,
2271
+ 7.6070e-02,
2272
+ -3.1384e-02,
2273
+ 6.7374e-02,
2274
+ -8.3764e-05,
2275
+ -2.1820e-02,
2276
+ 1.4192e-02,
2277
+ 2.3975e-02,
2278
+ 2.3384e-02,
2279
+ -2.3589e-02,
2280
+ -1.7196e-01,
2281
+ 1.4986e-01,
2282
+ 1.1465e-01,
2283
+ 2.0120e-02,
2284
+ 4.8034e-02,
2285
+ -3.9982e-02,
2286
+ -1.8704e-01,
2287
+ 6.2222e-02,
2288
+ -1.4799e-02,
2289
+ -1.0783e-01,
2290
+ 5.4098e-02,
2291
+ -1.2046e-02,
2292
+ -1.5444e-02,
2293
+ -1.4001e-01,
2294
+ 1.5944e-02,
2295
+ 7.9476e-02,
2296
+ -9.7107e-02,
2297
+ 1.0629e-01,
2298
+ 7.1731e-02,
2299
+ -8.1614e-03,
2300
+ 6.2800e-02,
2301
+ 5.5151e-02,
2302
+ -1.0514e-01,
2303
+ 1.7916e-02,
2304
+ 8.1229e-02,
2305
+ -7.8761e-02,
2306
+ 1.1222e-01,
2307
+ -1.1032e-01,
2308
+ -4.8039e-02,
2309
+ -1.5340e-01,
2310
+ 6.0481e-02,
2311
+ 3.2276e-02,
2312
+ -6.2797e-02,
2313
+ -3.3934e-02,
2314
+ 1.3062e-01,
2315
+ -6.3068e-03,
2316
+ -6.5936e-02,
2317
+ 4.2361e-02,
2318
+ -4.6102e-02,
2319
+ 1.2669e-01,
2320
+ 6.1909e-02,
2321
+ -5.7771e-02,
2322
+ 7.8385e-02,
2323
+ 2.4836e-01,
2324
+ 7.5433e-02,
2325
+ -2.0151e-02,
2326
+ -1.2117e-01,
2327
+ -4.1874e-02,
2328
+ -1.1751e-01,
2329
+ -1.4471e-01,
2330
+ 6.3096e-02,
2331
+ 2.9419e-02,
2332
+ 1.0021e-01,
2333
+ -1.2180e-02,
2334
+ 5.0896e-02,
2335
+ -4.0094e-02,
2336
+ 4.1059e-02,
2337
+ 8.0853e-02,
2338
+ -7.4614e-02,
2339
+ 7.5243e-02,
2340
+ 1.4928e-02,
2341
+ 1.1477e-02,
2342
+ 1.2385e-01,
2343
+ -1.4719e-02,
2344
+ 5.3751e-02,
2345
+ 5.2591e-02,
2346
+ 7.2013e-02,
2347
+ -1.6333e-02,
2348
+ -5.0256e-02,
2349
+ 1.4199e-01,
2350
+ -1.3433e-01,
2351
+ -7.1574e-02,
2352
+ -7.4449e-02,
2353
+ 1.6136e-01,
2354
+ 1.5659e-01,
2355
+ 2.1466e-02,
2356
+ 6.9775e-02,
2357
+ 1.0352e-01,
2358
+ -1.9641e-01,
2359
+ 1.3156e-01,
2360
+ 5.5903e-02,
2361
+ ],
2362
+ [
2363
+ 4.4988e-02,
2364
+ 1.0390e-01,
2365
+ 1.1266e-01,
2366
+ 1.1661e-01,
2367
+ 2.1212e-02,
2368
+ 1.4641e-02,
2369
+ -3.1484e-02,
2370
+ -8.3443e-02,
2371
+ -4.4991e-02,
2372
+ 1.5687e-01,
2373
+ 1.3158e-01,
2374
+ -3.3434e-02,
2375
+ 6.5104e-02,
2376
+ 2.0808e-02,
2377
+ -7.0680e-02,
2378
+ -4.2508e-02,
2379
+ 1.4304e-01,
2380
+ 9.8483e-02,
2381
+ -2.6904e-02,
2382
+ 3.2902e-02,
2383
+ 1.1152e-02,
2384
+ 2.1118e-02,
2385
+ -1.4842e-02,
2386
+ -2.2356e-02,
2387
+ -8.6942e-02,
2388
+ 2.6609e-02,
2389
+ -6.2533e-02,
2390
+ 1.7495e-01,
2391
+ -1.0246e-02,
2392
+ 1.2697e-01,
2393
+ 7.9345e-02,
2394
+ 1.0189e-01,
2395
+ -1.7178e-01,
2396
+ 7.5006e-02,
2397
+ 4.5051e-02,
2398
+ -1.0753e-01,
2399
+ 1.2829e-01,
2400
+ -3.3422e-02,
2401
+ 7.8522e-02,
2402
+ -4.5604e-02,
2403
+ 5.2822e-02,
2404
+ 8.3934e-03,
2405
+ -1.4676e-02,
2406
+ 1.0482e-02,
2407
+ -6.9389e-03,
2408
+ 2.2131e-03,
2409
+ -2.0089e-02,
2410
+ -1.7261e-01,
2411
+ 1.5468e-01,
2412
+ 1.1537e-01,
2413
+ 2.7489e-02,
2414
+ 2.9948e-02,
2415
+ -7.4234e-02,
2416
+ -1.9946e-01,
2417
+ 7.4632e-02,
2418
+ 2.2249e-03,
2419
+ -9.7061e-02,
2420
+ 6.4988e-02,
2421
+ -1.1239e-02,
2422
+ -2.6152e-02,
2423
+ -1.6496e-01,
2424
+ 3.0305e-02,
2425
+ 1.0491e-01,
2426
+ -6.7692e-02,
2427
+ 7.3323e-02,
2428
+ 7.0258e-02,
2429
+ 1.2020e-02,
2430
+ 4.1457e-02,
2431
+ 3.7126e-03,
2432
+ -1.0465e-01,
2433
+ 2.7035e-02,
2434
+ 8.1376e-02,
2435
+ -4.0890e-02,
2436
+ 1.0791e-01,
2437
+ -1.2141e-01,
2438
+ -1.6070e-02,
2439
+ -1.7111e-01,
2440
+ 7.4218e-02,
2441
+ -2.4698e-02,
2442
+ -3.7496e-02,
2443
+ -4.5666e-02,
2444
+ 1.0478e-01,
2445
+ -1.7289e-03,
2446
+ -7.1823e-02,
2447
+ 2.7480e-02,
2448
+ -5.5305e-02,
2449
+ 1.2323e-01,
2450
+ 5.3008e-02,
2451
+ -5.6903e-02,
2452
+ 6.6087e-02,
2453
+ 2.1523e-01,
2454
+ 8.2657e-02,
2455
+ -9.3275e-03,
2456
+ -1.3791e-01,
2457
+ -6.7856e-02,
2458
+ -1.4959e-01,
2459
+ -1.1916e-01,
2460
+ 6.2014e-02,
2461
+ 3.8735e-02,
2462
+ 9.7171e-02,
2463
+ 2.6820e-02,
2464
+ 7.4336e-02,
2465
+ -2.9922e-02,
2466
+ 5.3645e-02,
2467
+ 1.0981e-01,
2468
+ -3.7215e-02,
2469
+ 9.4254e-02,
2470
+ 4.2902e-02,
2471
+ -2.0969e-03,
2472
+ 1.1838e-01,
2473
+ -5.4015e-02,
2474
+ 5.3799e-02,
2475
+ 4.8568e-02,
2476
+ 8.0494e-02,
2477
+ -2.1410e-02,
2478
+ -2.0629e-02,
2479
+ 1.3477e-01,
2480
+ -1.4177e-01,
2481
+ -1.1144e-01,
2482
+ -5.1781e-02,
2483
+ 1.5780e-01,
2484
+ 1.9357e-01,
2485
+ 2.5627e-02,
2486
+ 7.5402e-02,
2487
+ 1.1982e-01,
2488
+ -1.8961e-01,
2489
+ 1.4646e-01,
2490
+ 4.2665e-02,
2491
+ ],
2492
+ [
2493
+ 5.1689e-02,
2494
+ 1.0737e-01,
2495
+ 1.1035e-01,
2496
+ 1.1308e-01,
2497
+ 2.0363e-02,
2498
+ 1.9703e-02,
2499
+ -3.2107e-02,
2500
+ -9.0838e-02,
2501
+ -3.7066e-02,
2502
+ 1.5839e-01,
2503
+ 1.3202e-01,
2504
+ -3.9661e-02,
2505
+ 6.3263e-02,
2506
+ 1.4509e-02,
2507
+ -6.0145e-02,
2508
+ -4.1459e-02,
2509
+ 1.4670e-01,
2510
+ 9.4701e-02,
2511
+ -2.7710e-02,
2512
+ 3.4281e-02,
2513
+ 1.2373e-02,
2514
+ 2.0087e-02,
2515
+ -2.0821e-02,
2516
+ -1.6274e-02,
2517
+ -7.9908e-02,
2518
+ 2.6209e-02,
2519
+ -7.0731e-02,
2520
+ 1.7365e-01,
2521
+ -8.5357e-03,
2522
+ 1.2784e-01,
2523
+ 7.9591e-02,
2524
+ 9.7291e-02,
2525
+ -1.6958e-01,
2526
+ 7.5774e-02,
2527
+ 4.4993e-02,
2528
+ -1.0499e-01,
2529
+ 1.2133e-01,
2530
+ -4.2564e-02,
2531
+ 7.7296e-02,
2532
+ -3.9049e-02,
2533
+ 5.4639e-02,
2534
+ 3.7831e-03,
2535
+ -1.2108e-02,
2536
+ 1.7782e-02,
2537
+ -1.8517e-03,
2538
+ 1.1160e-02,
2539
+ -1.9819e-02,
2540
+ -1.7390e-01,
2541
+ 1.5853e-01,
2542
+ 1.1225e-01,
2543
+ 2.6839e-02,
2544
+ 3.1746e-02,
2545
+ -8.1013e-02,
2546
+ -1.9950e-01,
2547
+ 7.7933e-02,
2548
+ 4.3883e-03,
2549
+ -9.1775e-02,
2550
+ 6.3761e-02,
2551
+ -2.0220e-02,
2552
+ -2.0873e-02,
2553
+ -1.6981e-01,
2554
+ 3.2587e-02,
2555
+ 1.0188e-01,
2556
+ -7.2338e-02,
2557
+ 7.9739e-02,
2558
+ 7.2336e-02,
2559
+ 1.2712e-02,
2560
+ 4.6304e-02,
2561
+ 1.1977e-02,
2562
+ -9.9069e-02,
2563
+ 3.2361e-02,
2564
+ 7.4647e-02,
2565
+ -3.2075e-02,
2566
+ 1.0834e-01,
2567
+ -1.2421e-01,
2568
+ -1.4408e-02,
2569
+ -1.7591e-01,
2570
+ 7.6520e-02,
2571
+ -2.1893e-02,
2572
+ -4.0591e-02,
2573
+ -3.9790e-02,
2574
+ 1.0072e-01,
2575
+ 2.1907e-03,
2576
+ -7.5756e-02,
2577
+ 3.4002e-02,
2578
+ -6.2571e-02,
2579
+ 1.2714e-01,
2580
+ 5.2011e-02,
2581
+ -5.6154e-02,
2582
+ 6.2876e-02,
2583
+ 2.0992e-01,
2584
+ 8.9547e-02,
2585
+ -1.2875e-02,
2586
+ -1.3528e-01,
2587
+ -6.6275e-02,
2588
+ -1.4322e-01,
2589
+ -1.0560e-01,
2590
+ 5.9555e-02,
2591
+ 4.4426e-02,
2592
+ 1.0047e-01,
2593
+ 3.3182e-02,
2594
+ 7.2001e-02,
2595
+ -3.6514e-02,
2596
+ 5.0708e-02,
2597
+ 1.0564e-01,
2598
+ -4.1174e-02,
2599
+ 1.0378e-01,
2600
+ 4.2651e-02,
2601
+ -2.6353e-03,
2602
+ 1.1964e-01,
2603
+ -6.2808e-02,
2604
+ 5.9555e-02,
2605
+ 5.4234e-02,
2606
+ 8.3802e-02,
2607
+ -1.2534e-02,
2608
+ -1.7949e-02,
2609
+ 1.3403e-01,
2610
+ -1.3682e-01,
2611
+ -1.1048e-01,
2612
+ -5.1154e-02,
2613
+ 1.5967e-01,
2614
+ 1.9743e-01,
2615
+ 3.2240e-02,
2616
+ 7.4049e-02,
2617
+ 1.2060e-01,
2618
+ -1.9087e-01,
2619
+ 1.3942e-01,
2620
+ 4.1168e-02,
2621
+ ],
2622
+ [
2623
+ 7.2669e-02,
2624
+ 1.0343e-01,
2625
+ 1.1966e-01,
2626
+ 9.3777e-02,
2627
+ -4.3358e-03,
2628
+ 5.0720e-02,
2629
+ -3.8923e-02,
2630
+ -9.1626e-02,
2631
+ -3.0056e-02,
2632
+ 1.6863e-01,
2633
+ 1.2676e-01,
2634
+ -5.3184e-02,
2635
+ 7.4056e-02,
2636
+ 1.5517e-02,
2637
+ -7.5657e-02,
2638
+ -3.7712e-02,
2639
+ 1.5280e-01,
2640
+ 9.0719e-02,
2641
+ -3.1311e-02,
2642
+ 1.7038e-02,
2643
+ 1.0062e-02,
2644
+ 1.4759e-02,
2645
+ -3.4438e-02,
2646
+ 1.5206e-02,
2647
+ -7.7897e-02,
2648
+ 2.7431e-02,
2649
+ -9.0602e-02,
2650
+ 1.7164e-01,
2651
+ -9.9721e-03,
2652
+ 1.4776e-01,
2653
+ 1.0475e-01,
2654
+ 9.0160e-02,
2655
+ -1.5270e-01,
2656
+ 7.4838e-02,
2657
+ 2.4463e-02,
2658
+ -1.0180e-01,
2659
+ 1.1501e-01,
2660
+ -5.0178e-02,
2661
+ 7.2532e-02,
2662
+ -1.8656e-02,
2663
+ 6.9202e-02,
2664
+ -9.6694e-03,
2665
+ -1.8219e-02,
2666
+ 3.5159e-02,
2667
+ 2.5262e-02,
2668
+ 3.4003e-02,
2669
+ -1.4053e-02,
2670
+ -1.7146e-01,
2671
+ 1.6299e-01,
2672
+ 1.0628e-01,
2673
+ 2.3901e-02,
2674
+ 4.2174e-02,
2675
+ -7.0690e-02,
2676
+ -1.8996e-01,
2677
+ 7.4249e-02,
2678
+ -2.3634e-03,
2679
+ -8.9371e-02,
2680
+ 5.7919e-02,
2681
+ -3.2949e-02,
2682
+ -2.4951e-03,
2683
+ -1.6144e-01,
2684
+ 3.1264e-02,
2685
+ 8.4885e-02,
2686
+ -9.7903e-02,
2687
+ 1.0806e-01,
2688
+ 7.3583e-02,
2689
+ 1.4389e-03,
2690
+ 6.4343e-02,
2691
+ 5.4305e-02,
2692
+ -9.2823e-02,
2693
+ 3.4699e-02,
2694
+ 6.5887e-02,
2695
+ -4.1142e-02,
2696
+ 1.1057e-01,
2697
+ -1.2077e-01,
2698
+ -3.4178e-02,
2699
+ -1.7040e-01,
2700
+ 7.2546e-02,
2701
+ 1.9399e-02,
2702
+ -6.3051e-02,
2703
+ -3.0156e-02,
2704
+ 1.1501e-01,
2705
+ 3.9456e-03,
2706
+ -7.8346e-02,
2707
+ 5.0548e-02,
2708
+ -6.3681e-02,
2709
+ 1.3261e-01,
2710
+ 6.1531e-02,
2711
+ -5.1743e-02,
2712
+ 6.4762e-02,
2713
+ 2.3264e-01,
2714
+ 9.4896e-02,
2715
+ -2.0536e-02,
2716
+ -1.1807e-01,
2717
+ -4.9545e-02,
2718
+ -1.1460e-01,
2719
+ -1.0079e-01,
2720
+ 5.7836e-02,
2721
+ 4.4144e-02,
2722
+ 1.1039e-01,
2723
+ 2.1311e-02,
2724
+ 4.8694e-02,
2725
+ -4.7126e-02,
2726
+ 3.7200e-02,
2727
+ 8.3108e-02,
2728
+ -7.0069e-02,
2729
+ 9.7451e-02,
2730
+ 2.0983e-02,
2731
+ -4.1628e-04,
2732
+ 1.2463e-01,
2733
+ -5.0752e-02,
2734
+ 7.0680e-02,
2735
+ 6.3588e-02,
2736
+ 8.6767e-02,
2737
+ 6.3361e-04,
2738
+ -2.5236e-02,
2739
+ 1.3658e-01,
2740
+ -1.1925e-01,
2741
+ -8.3861e-02,
2742
+ -6.3728e-02,
2743
+ 1.6767e-01,
2744
+ 1.7967e-01,
2745
+ 3.3894e-02,
2746
+ 7.2830e-02,
2747
+ 1.1590e-01,
2748
+ -1.9359e-01,
2749
+ 1.2254e-01,
2750
+ 4.7767e-02,
2751
+ ],
2752
+ [
2753
+ 8.4189e-02,
2754
+ 9.3299e-02,
2755
+ 1.1532e-01,
2756
+ 8.4670e-02,
2757
+ -9.7533e-03,
2758
+ 6.4423e-02,
2759
+ -4.3742e-02,
2760
+ -9.0334e-02,
2761
+ -1.9307e-02,
2762
+ 1.7434e-01,
2763
+ 1.2522e-01,
2764
+ -6.4064e-02,
2765
+ 7.7841e-02,
2766
+ 2.5594e-02,
2767
+ -9.4764e-02,
2768
+ -3.8088e-02,
2769
+ 1.5148e-01,
2770
+ 8.7002e-02,
2771
+ -3.8634e-02,
2772
+ 1.6073e-02,
2773
+ 1.0152e-02,
2774
+ 8.9035e-03,
2775
+ -3.9583e-02,
2776
+ 3.3614e-02,
2777
+ -7.4457e-02,
2778
+ 2.6379e-02,
2779
+ -9.8885e-02,
2780
+ 1.6510e-01,
2781
+ -1.1651e-02,
2782
+ 1.5700e-01,
2783
+ 1.3055e-01,
2784
+ 7.7680e-02,
2785
+ -1.3690e-01,
2786
+ 7.0143e-02,
2787
+ 5.4565e-03,
2788
+ -1.0524e-01,
2789
+ 1.1096e-01,
2790
+ -4.6091e-02,
2791
+ 6.8167e-02,
2792
+ -5.6453e-03,
2793
+ 7.2094e-02,
2794
+ -1.8229e-02,
2795
+ -2.5539e-02,
2796
+ 3.9328e-02,
2797
+ 4.3699e-02,
2798
+ 4.4686e-02,
2799
+ -1.2072e-02,
2800
+ -1.6738e-01,
2801
+ 1.6301e-01,
2802
+ 1.0474e-01,
2803
+ 1.4918e-02,
2804
+ 5.4164e-02,
2805
+ -7.2489e-02,
2806
+ -1.7993e-01,
2807
+ 7.7292e-02,
2808
+ -2.8187e-03,
2809
+ -8.9506e-02,
2810
+ 5.6049e-02,
2811
+ -3.1474e-02,
2812
+ 4.7455e-03,
2813
+ -1.5338e-01,
2814
+ 2.2827e-02,
2815
+ 6.7546e-02,
2816
+ -1.1578e-01,
2817
+ 1.2026e-01,
2818
+ 8.2049e-02,
2819
+ -1.1262e-02,
2820
+ 7.4682e-02,
2821
+ 7.3023e-02,
2822
+ -8.6490e-02,
2823
+ 3.1955e-02,
2824
+ 6.6658e-02,
2825
+ -4.6417e-02,
2826
+ 1.0408e-01,
2827
+ -1.1210e-01,
2828
+ -3.3793e-02,
2829
+ -1.6235e-01,
2830
+ 7.3353e-02,
2831
+ 4.5024e-02,
2832
+ -7.6526e-02,
2833
+ -3.0443e-02,
2834
+ 1.2222e-01,
2835
+ 1.1513e-02,
2836
+ -8.5835e-02,
2837
+ 5.6084e-02,
2838
+ -6.5380e-02,
2839
+ 1.3535e-01,
2840
+ 7.2861e-02,
2841
+ -4.8121e-02,
2842
+ 6.6781e-02,
2843
+ 2.5041e-01,
2844
+ 9.3076e-02,
2845
+ -2.0334e-02,
2846
+ -1.0416e-01,
2847
+ -3.9805e-02,
2848
+ -1.0051e-01,
2849
+ -9.2133e-02,
2850
+ 6.3271e-02,
2851
+ 3.4768e-02,
2852
+ 1.1286e-01,
2853
+ 1.3493e-02,
2854
+ 3.0643e-02,
2855
+ -5.5867e-02,
2856
+ 2.4630e-02,
2857
+ 7.8038e-02,
2858
+ -7.5769e-02,
2859
+ 9.0636e-02,
2860
+ 1.8840e-02,
2861
+ 1.3688e-02,
2862
+ 1.2780e-01,
2863
+ -4.3306e-02,
2864
+ 7.6513e-02,
2865
+ 5.6990e-02,
2866
+ 8.7927e-02,
2867
+ 1.6438e-03,
2868
+ -1.0833e-02,
2869
+ 1.3722e-01,
2870
+ -9.4365e-02,
2871
+ -7.4534e-02,
2872
+ -6.2697e-02,
2873
+ 1.6868e-01,
2874
+ 1.6850e-01,
2875
+ 4.3229e-02,
2876
+ 7.4762e-02,
2877
+ 1.1225e-01,
2878
+ -1.9559e-01,
2879
+ 1.1981e-01,
2880
+ 5.0025e-02,
2881
+ ],
2882
+ [
2883
+ 1.0233e-01,
2884
+ 6.7397e-02,
2885
+ 1.0132e-01,
2886
+ 1.1631e-01,
2887
+ 4.3957e-02,
2888
+ 5.7017e-02,
2889
+ -4.0769e-02,
2890
+ -1.2329e-01,
2891
+ -6.1235e-03,
2892
+ 1.3821e-01,
2893
+ 1.3135e-01,
2894
+ -7.2640e-02,
2895
+ 6.7380e-02,
2896
+ 4.7518e-02,
2897
+ -1.0261e-01,
2898
+ -8.4497e-02,
2899
+ 1.1074e-01,
2900
+ 7.9965e-02,
2901
+ 4.3208e-03,
2902
+ -7.9881e-03,
2903
+ 3.0558e-02,
2904
+ 3.8130e-02,
2905
+ -7.0863e-02,
2906
+ 2.6484e-02,
2907
+ -8.1795e-02,
2908
+ 3.9067e-02,
2909
+ -1.5670e-01,
2910
+ 1.2963e-01,
2911
+ 4.3868e-03,
2912
+ 1.3081e-01,
2913
+ 1.7308e-01,
2914
+ 5.3891e-02,
2915
+ -1.2683e-01,
2916
+ 6.0787e-02,
2917
+ -7.8336e-02,
2918
+ -1.0347e-01,
2919
+ 5.8472e-02,
2920
+ -2.7212e-02,
2921
+ 7.1385e-02,
2922
+ -6.8297e-03,
2923
+ 8.7485e-02,
2924
+ -7.1364e-02,
2925
+ -1.9182e-02,
2926
+ 8.7217e-02,
2927
+ 8.2944e-02,
2928
+ 3.4400e-02,
2929
+ -8.5778e-03,
2930
+ -1.1191e-01,
2931
+ 1.4871e-01,
2932
+ 1.0602e-01,
2933
+ 5.5060e-02,
2934
+ 4.0554e-02,
2935
+ -1.0835e-01,
2936
+ -1.4749e-01,
2937
+ 1.1336e-01,
2938
+ 8.1897e-03,
2939
+ -5.1693e-02,
2940
+ 4.9600e-02,
2941
+ -3.6377e-02,
2942
+ -1.6864e-03,
2943
+ -1.4149e-01,
2944
+ -3.8906e-02,
2945
+ 5.3481e-02,
2946
+ -1.1160e-01,
2947
+ 1.3861e-01,
2948
+ 6.3040e-02,
2949
+ -2.2296e-02,
2950
+ 8.1680e-02,
2951
+ 7.9567e-02,
2952
+ -6.1083e-02,
2953
+ 6.7573e-02,
2954
+ 9.8196e-02,
2955
+ -7.0956e-02,
2956
+ 8.7717e-02,
2957
+ -1.3439e-01,
2958
+ -4.4087e-02,
2959
+ -1.7677e-01,
2960
+ 5.1971e-02,
2961
+ 1.1013e-01,
2962
+ -1.2838e-01,
2963
+ -1.8450e-02,
2964
+ 9.7373e-02,
2965
+ 1.5415e-02,
2966
+ -6.8535e-02,
2967
+ 5.8374e-02,
2968
+ -1.0450e-01,
2969
+ 1.3363e-01,
2970
+ 1.2089e-01,
2971
+ -4.1817e-02,
2972
+ 3.1015e-02,
2973
+ 2.7185e-01,
2974
+ 6.9890e-02,
2975
+ -3.8259e-03,
2976
+ -7.7094e-02,
2977
+ 2.4651e-02,
2978
+ -1.0086e-01,
2979
+ -8.4380e-02,
2980
+ 4.9062e-02,
2981
+ 4.7470e-02,
2982
+ 1.0739e-01,
2983
+ -2.4693e-03,
2984
+ -5.9990e-02,
2985
+ -6.2990e-02,
2986
+ 1.2398e-03,
2987
+ 4.8100e-03,
2988
+ -6.0338e-02,
2989
+ 6.4579e-02,
2990
+ 4.2505e-04,
2991
+ -2.9926e-02,
2992
+ 1.3627e-01,
2993
+ -3.0724e-02,
2994
+ 4.2972e-02,
2995
+ 4.6971e-02,
2996
+ 1.2441e-01,
2997
+ -2.4336e-02,
2998
+ 6.9954e-02,
2999
+ 1.0403e-01,
3000
+ -3.2450e-02,
3001
+ -4.3154e-02,
3002
+ -4.9959e-02,
3003
+ 1.5666e-01,
3004
+ 1.3688e-01,
3005
+ 5.3450e-02,
3006
+ 7.9968e-02,
3007
+ 1.3858e-01,
3008
+ -1.6817e-01,
3009
+ 1.2637e-01,
3010
+ 7.3937e-02,
3011
+ ],
3012
+ ]
3013
+ )
backend/vespa_app.py ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ from typing import Any, Dict, Tuple
4
+ import asyncio
5
+ import numpy as np
6
+ import torch
7
+ from dotenv import load_dotenv
8
+ from vespa.application import Vespa
9
+ from vespa.io import VespaQueryResponse
10
+ from .colpali import SimMapGenerator
11
+ import backend.stopwords
12
+ import logging
13
+
14
+
15
+ class VespaQueryClient:
16
+ MAX_QUERY_TERMS = 64
17
+ VESPA_SCHEMA_NAME = "pdf_page"
18
+ SELECT_FIELDS = "id,title,url,blur_image,page_number,snippet,text"
19
+
20
+ def __init__(self, logger: logging.Logger):
21
+ """
22
+ Initialize the VespaQueryClient by loading environment variables and establishing a connection to the Vespa application.
23
+ """
24
+ load_dotenv()
25
+ self.logger = logger
26
+
27
+ if os.environ.get("USE_MTLS") == "true":
28
+ self.logger.info("Connected using mTLS")
29
+ mtls_key = os.environ.get("VESPA_CLOUD_MTLS_KEY")
30
+ mtls_cert = os.environ.get("VESPA_CLOUD_MTLS_CERT")
31
+
32
+ self.vespa_app_url = os.environ.get("VESPA_APP_MTLS_URL")
33
+ if not self.vespa_app_url:
34
+ raise ValueError(
35
+ "Please set the VESPA_APP_MTLS_URL environment variable"
36
+ )
37
+
38
+ if not mtls_cert or not mtls_key:
39
+ raise ValueError(
40
+ "USE_MTLS was true, but VESPA_CLOUD_MTLS_KEY and VESPA_CLOUD_MTLS_CERT were not set"
41
+ )
42
+
43
+ # write the key and cert to a file
44
+ mtls_key_path = "/tmp/vespa-data-plane-private-key.pem"
45
+ with open(mtls_key_path, "w") as f:
46
+ f.write(mtls_key)
47
+
48
+ mtls_cert_path = "/tmp/vespa-data-plane-public-cert.pem"
49
+ with open(mtls_cert_path, "w") as f:
50
+ f.write(mtls_cert)
51
+
52
+ # Instantiate Vespa connection
53
+ self.app = Vespa(
54
+ url=self.vespa_app_url, key=mtls_key_path, cert=mtls_cert_path
55
+ )
56
+ else:
57
+ self.logger.info("Connected using token")
58
+ self.vespa_app_url = os.environ.get("VESPA_APP_TOKEN_URL")
59
+ if not self.vespa_app_url:
60
+ raise ValueError(
61
+ "Please set the VESPA_APP_TOKEN_URL environment variable"
62
+ )
63
+
64
+ self.vespa_cloud_secret_token = os.environ.get("VESPA_CLOUD_SECRET_TOKEN")
65
+
66
+ if not self.vespa_cloud_secret_token:
67
+ raise ValueError(
68
+ "Please set the VESPA_CLOUD_SECRET_TOKEN environment variable"
69
+ )
70
+
71
+ # Instantiate Vespa connection
72
+ self.app = Vespa(
73
+ url=self.vespa_app_url,
74
+ vespa_cloud_secret_token=self.vespa_cloud_secret_token,
75
+ )
76
+
77
+ self.app.wait_for_application_up()
78
+ self.logger.info(f"Connected to Vespa at {self.vespa_app_url}")
79
+
80
+ def get_fields(self, sim_map: bool = False):
81
+ if not sim_map:
82
+ return self.SELECT_FIELDS
83
+ else:
84
+ return "summaryfeatures"
85
+
86
+ def format_query_results(
87
+ self, query: str, response: VespaQueryResponse, hits: int = 5
88
+ ) -> dict:
89
+ """
90
+ Format the Vespa query results.
91
+
92
+ Args:
93
+ query (str): The query text.
94
+ response (VespaQueryResponse): The response from Vespa.
95
+ hits (int, optional): Number of hits to display. Defaults to 5.
96
+
97
+ Returns:
98
+ dict: The JSON content of the response.
99
+ """
100
+ query_time = response.json.get("timing", {}).get("searchtime", -1)
101
+ query_time = round(query_time, 2)
102
+ count = response.json.get("root", {}).get("fields", {}).get("totalCount", 0)
103
+ result_text = f"Query text: '{query}', query time {query_time}s, count={count}, top results:\n"
104
+ self.logger.debug(result_text)
105
+ return response.json
106
+
107
+ async def query_vespa_bm25(
108
+ self,
109
+ query: str,
110
+ q_emb: torch.Tensor,
111
+ hits: int = 3,
112
+ timeout: str = "10s",
113
+ sim_map: bool = False,
114
+ **kwargs,
115
+ ) -> dict:
116
+ """
117
+ Query Vespa using the BM25 ranking profile.
118
+ This corresponds to the "BM25" radio button in the UI.
119
+
120
+ Args:
121
+ query (str): The query text.
122
+ q_emb (torch.Tensor): Query embeddings.
123
+ hits (int, optional): Number of hits to retrieve. Defaults to 3.
124
+ timeout (str, optional): Query timeout. Defaults to "10s".
125
+
126
+ Returns:
127
+ dict: The formatted query results.
128
+ """
129
+ async with self.app.asyncio(connections=1) as session:
130
+ query_embedding = self.format_q_embs(q_emb)
131
+
132
+ start = time.perf_counter()
133
+ response: VespaQueryResponse = await session.query(
134
+ body={
135
+ "yql": (
136
+ f"select {self.get_fields(sim_map=sim_map)} from {self.VESPA_SCHEMA_NAME} where userQuery();"
137
+ ),
138
+ "ranking": self.get_rank_profile("bm25", sim_map),
139
+ "query": query,
140
+ "timeout": timeout,
141
+ "hits": hits,
142
+ "input.query(qt)": query_embedding,
143
+ "presentation.timing": True,
144
+ **kwargs,
145
+ },
146
+ )
147
+ assert response.is_successful(), response.json
148
+ stop = time.perf_counter()
149
+ self.logger.debug(
150
+ f"Query time + data transfer took: {stop - start} s, Vespa reported searchtime was "
151
+ f"{response.json.get('timing', {}).get('searchtime', -1)} s"
152
+ )
153
+ return self.format_query_results(query, response)
154
+
155
+ def float_to_binary_embedding(self, float_query_embedding: dict) -> dict:
156
+ """
157
+ Convert float query embeddings to binary embeddings.
158
+
159
+ Args:
160
+ float_query_embedding (dict): Dictionary of float embeddings.
161
+
162
+ Returns:
163
+ dict: Dictionary of binary embeddings.
164
+ """
165
+ binary_query_embeddings = {}
166
+ for key, vector in float_query_embedding.items():
167
+ binary_vector = (
168
+ np.packbits(np.where(np.array(vector) > 0, 1, 0))
169
+ .astype(np.int8)
170
+ .tolist()
171
+ )
172
+ binary_query_embeddings[key] = binary_vector
173
+ if len(binary_query_embeddings) >= self.MAX_QUERY_TERMS:
174
+ self.logger.warning(
175
+ f"Warning: Query has more than {self.MAX_QUERY_TERMS} terms. Truncating."
176
+ )
177
+ break
178
+ return binary_query_embeddings
179
+
180
+ def create_nn_query_strings(
181
+ self, binary_query_embeddings: dict, target_hits_per_query_tensor: int = 20
182
+ ) -> Tuple[str, dict]:
183
+ """
184
+ Create nearest neighbor query strings for Vespa.
185
+
186
+ Args:
187
+ binary_query_embeddings (dict): Binary query embeddings.
188
+ target_hits_per_query_tensor (int, optional): Target hits per query tensor. Defaults to 20.
189
+
190
+ Returns:
191
+ Tuple[str, dict]: Nearest neighbor query string and query tensor dictionary.
192
+ """
193
+ nn_query_dict = {}
194
+ for i in range(len(binary_query_embeddings)):
195
+ nn_query_dict[f"input.query(rq{i})"] = binary_query_embeddings[i]
196
+ nn = " OR ".join(
197
+ [
198
+ f"({{targetHits:{target_hits_per_query_tensor}}}nearestNeighbor(embedding,rq{i}))"
199
+ for i in range(len(binary_query_embeddings))
200
+ ]
201
+ )
202
+ return nn, nn_query_dict
203
+
204
+ def format_q_embs(self, q_embs: torch.Tensor) -> dict:
205
+ """
206
+ Convert query embeddings to a dictionary of lists.
207
+
208
+ Args:
209
+ q_embs (torch.Tensor): Query embeddings tensor.
210
+
211
+ Returns:
212
+ dict: Dictionary where each key is an index and value is the embedding list.
213
+ """
214
+ return {idx: emb.tolist() for idx, emb in enumerate(q_embs)}
215
+
216
+ async def get_result_from_query(
217
+ self,
218
+ query: str,
219
+ q_embs: torch.Tensor,
220
+ ranking: str,
221
+ idx_to_token: dict,
222
+ ) -> Dict[str, Any]:
223
+ """
224
+ Get query results from Vespa based on the ranking method.
225
+
226
+ Args:
227
+ query (str): The query text.
228
+ q_embs (torch.Tensor): Query embeddings.
229
+ ranking (str): The ranking method to use.
230
+ idx_to_token (dict): Index to token mapping.
231
+
232
+ Returns:
233
+ Dict[str, Any]: The query results.
234
+ """
235
+
236
+ # Remove stopwords from the query to avoid visual emphasis on irrelevant words (e.g., "the", "and", "of")
237
+ query = backend.stopwords.filter(query)
238
+
239
+ rank_method = ranking.split("_")[0]
240
+ sim_map: bool = len(ranking.split("_")) > 1 and ranking.split("_")[1] == "sim"
241
+ if rank_method == "colpali": # ColPali
242
+ result = await self.query_vespa_colpali(
243
+ query=query, ranking=rank_method, q_emb=q_embs, sim_map=sim_map
244
+ )
245
+ elif rank_method == "hybrid": # Hybrid ColPali+BM25
246
+ result = await self.query_vespa_colpali(
247
+ query=query, ranking=rank_method, q_emb=q_embs, sim_map=sim_map
248
+ )
249
+ elif rank_method == "bm25":
250
+ result = await self.query_vespa_bm25(query, q_embs, sim_map=sim_map)
251
+ else:
252
+ raise ValueError(f"Unsupported ranking: {rank_method}")
253
+ if "root" not in result or "children" not in result["root"]:
254
+ result["root"] = {"children": []}
255
+ return result
256
+ for single_result in result["root"]["children"]:
257
+ self.logger.debug(single_result["fields"].keys())
258
+ return result
259
+
260
+ def get_sim_maps_from_query(
261
+ self, query: str, q_embs: torch.Tensor, ranking: str, idx_to_token: dict
262
+ ):
263
+ """
264
+ Get similarity maps from Vespa based on the ranking method.
265
+
266
+ Args:
267
+ query (str): The query text.
268
+ q_embs (torch.Tensor): Query embeddings.
269
+ ranking (str): The ranking method to use.
270
+ idx_to_token (dict): Index to token mapping.
271
+
272
+ Returns:
273
+ Dict[str, Any]: The query results.
274
+ """
275
+ # Get the result by calling asyncio.run
276
+ result = asyncio.run(
277
+ self.get_result_from_query(query, q_embs, ranking, idx_to_token)
278
+ )
279
+ vespa_sim_maps = []
280
+ for single_result in result["root"]["children"]:
281
+ vespa_sim_map = single_result["fields"].get("summaryfeatures", None)
282
+ if vespa_sim_map is not None:
283
+ vespa_sim_maps.append(vespa_sim_map)
284
+ else:
285
+ raise ValueError("No sim_map found in Vespa response")
286
+ return vespa_sim_maps
287
+
288
+ async def get_full_image_from_vespa(self, doc_id: str) -> str:
289
+ """
290
+ Retrieve the full image from Vespa for a given document ID.
291
+
292
+ Args:
293
+ doc_id (str): The document ID.
294
+
295
+ Returns:
296
+ str: The full image data.
297
+ """
298
+ async with self.app.asyncio(connections=1) as session:
299
+ start = time.perf_counter()
300
+ response: VespaQueryResponse = await session.query(
301
+ body={
302
+ "yql": f'select full_image from {self.VESPA_SCHEMA_NAME} where id contains "{doc_id}"',
303
+ "ranking": "unranked",
304
+ "presentation.timing": True,
305
+ "ranking.matching.numThreadsPerSearch": 1,
306
+ },
307
+ )
308
+ assert response.is_successful(), response.json
309
+ stop = time.perf_counter()
310
+ self.logger.debug(
311
+ f"Getting image from Vespa took: {stop - start} s, Vespa reported searchtime was "
312
+ f"{response.json.get('timing', {}).get('searchtime', -1)} s"
313
+ )
314
+ return response.json["root"]["children"][0]["fields"]["full_image"]
315
+
316
+ def get_results_children(self, result: VespaQueryResponse) -> list:
317
+ return result["root"]["children"]
318
+
319
+ def results_to_search_results(
320
+ self, result: VespaQueryResponse, idx_to_token: dict
321
+ ) -> list:
322
+ # Initialize sim_map_ fields in the result
323
+ fields_to_add = [
324
+ f"sim_map_{token}_{idx}"
325
+ for idx, token in idx_to_token.items()
326
+ if not SimMapGenerator.should_filter_token(token)
327
+ ]
328
+ for child in result["root"]["children"]:
329
+ for sim_map_key in fields_to_add:
330
+ child["fields"][sim_map_key] = None
331
+ return self.get_results_children(result)
332
+
333
+ async def get_suggestions(self, query: str) -> list:
334
+ async with self.app.asyncio(connections=1) as session:
335
+ start = time.perf_counter()
336
+ yql = f'select questions from {self.VESPA_SCHEMA_NAME} where questions matches (".*{query}.*")'
337
+ response: VespaQueryResponse = await session.query(
338
+ body={
339
+ "yql": yql,
340
+ "query": query,
341
+ "ranking": "unranked",
342
+ "presentation.timing": True,
343
+ "presentation.summary": "suggestions",
344
+ "ranking.matching.numThreadsPerSearch": 1,
345
+ },
346
+ )
347
+ assert response.is_successful(), response.json
348
+ stop = time.perf_counter()
349
+ self.logger.debug(
350
+ f"Getting suggestions from Vespa took: {stop - start} s, Vespa reported searchtime was "
351
+ f"{response.json.get('timing', {}).get('searchtime', -1)} s"
352
+ )
353
+ search_results = (
354
+ response.json["root"]["children"]
355
+ if "root" in response.json and "children" in response.json["root"]
356
+ else []
357
+ )
358
+ questions = [
359
+ result["fields"]["questions"]
360
+ for result in search_results
361
+ if "questions" in result["fields"]
362
+ ]
363
+
364
+ unique_questions = set([item for sublist in questions for item in sublist])
365
+
366
+ # remove an artifact from our data generation
367
+ if "string" in unique_questions:
368
+ unique_questions.remove("string")
369
+
370
+ return list(unique_questions)
371
+
372
+ def get_rank_profile(self, ranking: str, sim_map: bool) -> str:
373
+ if sim_map:
374
+ return f"{ranking}_sim"
375
+ else:
376
+ return ranking
377
+
378
+ async def query_vespa_colpali(
379
+ self,
380
+ query: str,
381
+ ranking: str,
382
+ q_emb: torch.Tensor,
383
+ target_hits_per_query_tensor: int = 100,
384
+ hnsw_explore_additional_hits: int = 300,
385
+ hits: int = 3,
386
+ timeout: str = "10s",
387
+ sim_map: bool = False,
388
+ **kwargs,
389
+ ) -> dict:
390
+ """
391
+ Query Vespa using nearest neighbor search with mixed tensors for MaxSim calculations.
392
+ This corresponds to the "ColPali" radio button in the UI.
393
+
394
+ Args:
395
+ query (str): The query text.
396
+ q_emb (torch.Tensor): Query embeddings.
397
+ target_hits_per_query_tensor (int, optional): Target hits per query tensor. Defaults to 20.
398
+ hits (int, optional): Number of hits to retrieve. Defaults to 3.
399
+ timeout (str, optional): Query timeout. Defaults to "10s".
400
+
401
+ Returns:
402
+ dict: The formatted query results.
403
+ """
404
+ async with self.app.asyncio(connections=1) as session:
405
+ float_query_embedding = self.format_q_embs(q_emb)
406
+ binary_query_embeddings = self.float_to_binary_embedding(
407
+ float_query_embedding
408
+ )
409
+
410
+ # Mixed tensors for MaxSim calculations
411
+ query_tensors = {
412
+ "input.query(qtb)": binary_query_embeddings,
413
+ "input.query(qt)": float_query_embedding,
414
+ }
415
+ nn_string, nn_query_dict = self.create_nn_query_strings(
416
+ binary_query_embeddings, target_hits_per_query_tensor
417
+ )
418
+ query_tensors.update(nn_query_dict)
419
+ response: VespaQueryResponse = await session.query(
420
+ body={
421
+ **query_tensors,
422
+ "presentation.timing": True,
423
+ "yql": (
424
+ f"select {self.get_fields(sim_map=sim_map)} from {self.VESPA_SCHEMA_NAME} where {nn_string} or userQuery()"
425
+ ),
426
+ "ranking.profile": self.get_rank_profile(
427
+ ranking=ranking, sim_map=sim_map
428
+ ),
429
+ "timeout": timeout,
430
+ "hits": hits,
431
+ "query": query,
432
+ "hnsw.exploreAdditionalHits": hnsw_explore_additional_hits,
433
+ "ranking.rerankCount": 100,
434
+ **kwargs,
435
+ },
436
+ )
437
+ assert response.is_successful(), response.json
438
+ return self.format_query_results(query, response)
439
+
440
+ async def keepalive(self) -> bool:
441
+ """
442
+ Query Vespa to keep the connection alive.
443
+
444
+ Returns:
445
+ bool: True if the connection is alive.
446
+ """
447
+ async with self.app.asyncio(connections=1) as session:
448
+ response: VespaQueryResponse = await session.query(
449
+ body={
450
+ "yql": f"select title from {self.VESPA_SCHEMA_NAME} where true limit 1;",
451
+ "ranking": "unranked",
452
+ "query": "keepalive",
453
+ "timeout": "3s",
454
+ "hits": 1,
455
+ },
456
+ )
457
+ assert response.is_successful(), response.json
458
+ return True
frontend/__init__.py ADDED
File without changes
frontend/app.py ADDED
@@ -0,0 +1,768 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ from urllib.parse import quote_plus
3
+
4
+ from fasthtml.components import (
5
+ H1,
6
+ H2,
7
+ H3,
8
+ Br,
9
+ Div,
10
+ Form,
11
+ Img,
12
+ NotStr,
13
+ P,
14
+ Hr,
15
+ Span,
16
+ A,
17
+ Script,
18
+ Button,
19
+ Label,
20
+ RadioGroup,
21
+ RadioGroupItem,
22
+ Separator,
23
+ Ul,
24
+ Li,
25
+ Strong,
26
+ Iframe,
27
+ )
28
+ from fasthtml.xtend import A, Script
29
+ from lucide_fasthtml import Lucide
30
+ from shad4fast import Badge, Button, Input, Label, RadioGroup, RadioGroupItem, Separator
31
+
32
+ # JavaScript to check the input value and enable/disable the search button and radio buttons
33
+ check_input_script = Script(
34
+ """
35
+ window.onload = function() {
36
+ const input = document.getElementById('search-input');
37
+ const button = document.querySelector('[data-button="search-button"]');
38
+ const radioGroupItems = document.querySelectorAll('button[data-ref="radio-item"]'); // Get all radio buttons
39
+
40
+ function checkInputValue() {
41
+ const isInputEmpty = input.value.trim() === "";
42
+ button.disabled = isInputEmpty; // Disable the submit button
43
+ radioGroupItems.forEach(item => {
44
+ item.disabled = isInputEmpty; // Disable/enable the radio buttons
45
+ });
46
+ }
47
+
48
+ input.addEventListener('input', checkInputValue); // Listen for input changes
49
+ checkInputValue(); // Initial check when the page loads
50
+ };
51
+ """
52
+ )
53
+
54
+ # JavaScript to handle the image swapping, reset button, and active class toggling
55
+ image_swapping = Script(
56
+ """
57
+ document.addEventListener('click', function (e) {
58
+ if (e.target.classList.contains('sim-map-button') || e.target.classList.contains('reset-button')) {
59
+ const imgContainer = e.target.closest('.relative');
60
+ const overlayContainer = imgContainer.querySelector('.overlay-container');
61
+ const newSrc = e.target.getAttribute('data-image-src');
62
+
63
+ // If it's a reset button, remove the overlay image
64
+ if (e.target.classList.contains('reset-button')) {
65
+ overlayContainer.innerHTML = ''; // Clear the overlay container, showing only the full image
66
+ } else {
67
+ // Create a new overlay image
68
+ const img = document.createElement('img');
69
+ img.src = newSrc;
70
+ img.classList.add('overlay-image', 'absolute', 'top-0', 'left-0', 'w-full', 'h-full');
71
+ overlayContainer.innerHTML = ''; // Clear any previous overlay
72
+ overlayContainer.appendChild(img); // Add the new overlay image
73
+ }
74
+
75
+ // Toggle active class on buttons
76
+ const activeButton = document.querySelector('.sim-map-button.active');
77
+ if (activeButton) {
78
+ activeButton.classList.remove('active');
79
+ }
80
+ if (e.target.classList.contains('sim-map-button')) {
81
+ e.target.classList.add('active');
82
+ }
83
+ }
84
+ });
85
+ """
86
+ )
87
+
88
+ toggle_text_content = Script(
89
+ """
90
+ function toggleTextContent(idx) {
91
+ const textColumn = document.getElementById(`text-column-${idx}`);
92
+ const imageTextColumns = document.getElementById(`image-text-columns-${idx}`);
93
+ const toggleButton = document.getElementById(`toggle-button-${idx}`);
94
+
95
+ if (textColumn.classList.contains('md-grid-text-column')) {
96
+ // Hide the text column
97
+ textColumn.classList.remove('md-grid-text-column');
98
+ imageTextColumns.classList.remove('grid-image-text-columns');
99
+ toggleButton.innerText = `Show Text`;
100
+ } else {
101
+ // Show the text column
102
+ textColumn.classList.add('md-grid-text-column');
103
+ imageTextColumns.classList.add('grid-image-text-columns');
104
+ toggleButton.innerText = `Hide Text`;
105
+ }
106
+ }
107
+ """
108
+ )
109
+
110
+ autocomplete_script = Script(
111
+ """
112
+ document.addEventListener('DOMContentLoaded', function() {
113
+ const input = document.querySelector('#search-input');
114
+ const awesomplete = new Awesomplete(input, { minChars: 1, maxItems: 5 });
115
+
116
+ input.addEventListener('input', function() {
117
+ if (this.value.length >= 1) {
118
+ // Use template literals to insert the input value dynamically in the query parameter
119
+ fetch(`/suggestions?query=${encodeURIComponent(this.value)}`)
120
+ .then(response => response.json())
121
+ .then(data => {
122
+ // Update the Awesomplete list dynamically with fetched suggestions
123
+ awesomplete.list = data.suggestions;
124
+ })
125
+ .catch(err => console.error('Error fetching suggestions:', err));
126
+ }
127
+ });
128
+ });
129
+ """
130
+ )
131
+
132
+ dynamic_elements_scrollbars = Script(
133
+ """
134
+ (function () {
135
+ const { applyOverlayScrollbars, getScrollbarTheme } = OverlayScrollbarsManager;
136
+
137
+ function applyScrollbarsToDynamicElements() {
138
+ const scrollbarTheme = getScrollbarTheme();
139
+
140
+ // Apply scrollbars to dynamically loaded result-text-full and result-text-snippet elements
141
+ const resultTextFullElements = document.querySelectorAll('[id^="result-text-full"]');
142
+ const resultTextSnippetElements = document.querySelectorAll('[id^="result-text-snippet"]');
143
+
144
+ resultTextFullElements.forEach(element => {
145
+ applyOverlayScrollbars(element, scrollbarTheme);
146
+ });
147
+
148
+ resultTextSnippetElements.forEach(element => {
149
+ applyOverlayScrollbars(element, scrollbarTheme);
150
+ });
151
+ }
152
+
153
+ // Apply scrollbars after dynamic content is loaded (e.g., after search results)
154
+ applyScrollbarsToDynamicElements();
155
+
156
+ // Observe changes in the 'dark' class to adjust the theme dynamically if needed
157
+ const observer = new MutationObserver(applyScrollbarsToDynamicElements);
158
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
159
+ })();
160
+ """
161
+ )
162
+
163
+ submit_form_on_radio_change = Script(
164
+ """
165
+ document.addEventListener('click', function (e) {
166
+ // if target has data-ref="radio-item" and type is button
167
+ if (e.target.getAttribute('data-ref') === 'radio-item' && e.target.type === 'button') {
168
+ console.log('Radio button clicked');
169
+ const form = e.target.closest('form');
170
+ form.submit();
171
+ }
172
+ });
173
+ """
174
+ )
175
+
176
+
177
+ def ShareButtons():
178
+ title = "Visual RAG over PDFs with Vespa and ColPali"
179
+ url = "https://huggingface.co/spaces/vespa-engine/colpali-vespa-visual-retrieval"
180
+ return Div(
181
+ A(
182
+ Img(src="/static/img/linkedin.svg", aria_hidden="true", cls="h-[21px]"),
183
+ "Share on LinkedIn",
184
+ href=f"https://www.linkedin.com/sharing/share-offsite/?url={quote_plus(url)}",
185
+ rel="noopener noreferrer",
186
+ target="_blank",
187
+ cls="bg-[#0A66C2] text-white inline-flex items-center gap-x-1.5 px-2.5 py-1.5 border rounded-md text-sm font-semibold",
188
+ ),
189
+ A(
190
+ Img(src="/static/img/x.svg", aria_hidden="true", cls="h-[21px]"),
191
+ "Share on X",
192
+ href=f"https://twitter.com/intent/tweet?text={quote_plus(title)}&url={quote_plus(url)}",
193
+ rel="noopener noreferrer",
194
+ target="_blank",
195
+ cls="bg-black text-white inline-flex items-center gap-x-1.5 px-2.5 py-1.5 border rounded-md text-sm font-semibold",
196
+ ),
197
+ cls="flex items-center justify-center space-x-8 mt-5",
198
+ )
199
+
200
+
201
+ def SearchBox(with_border=False, query_value="", ranking_value="hybrid"):
202
+ grid_cls = "grid gap-2 items-center p-3 bg-muted w-full"
203
+
204
+ if with_border:
205
+ grid_cls = "grid gap-2 p-3 rounded-md border border-input bg-muted w-full ring-offset-background focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:border-input"
206
+
207
+ return Form(
208
+ Div(
209
+ Lucide(
210
+ icon="search", cls="absolute left-2 top-2 text-muted-foreground z-10"
211
+ ),
212
+ Input(
213
+ placeholder="Enter your search query...",
214
+ name="query",
215
+ value=query_value,
216
+ id="search-input",
217
+ cls="text-base pl-10 border-transparent ring-offset-transparent ring-0 focus-visible:ring-transparent bg-white dark:bg-background awesomplete",
218
+ data_list="#suggestions",
219
+ style="font-size: 1rem",
220
+ autofocus=True,
221
+ ),
222
+ cls="relative",
223
+ ),
224
+ Div(
225
+ Div(
226
+ Span("Ranking by:", cls="text-muted-foreground text-xs font-semibold"),
227
+ RadioGroup(
228
+ Div(
229
+ RadioGroupItem(value="colpali", id="colpali"),
230
+ Label("ColPali", htmlFor="ColPali"),
231
+ cls="flex items-center space-x-2",
232
+ ),
233
+ Div(
234
+ RadioGroupItem(value="bm25", id="bm25"),
235
+ Label("BM25", htmlFor="BM25"),
236
+ cls="flex items-center space-x-2",
237
+ ),
238
+ Div(
239
+ RadioGroupItem(value="hybrid", id="hybrid"),
240
+ Label("Hybrid ColPali + BM25", htmlFor="Hybrid ColPali + BM25"),
241
+ cls="flex items-center space-x-2",
242
+ ),
243
+ name="ranking",
244
+ default_value=ranking_value,
245
+ cls="grid-flow-col gap-x-5 text-muted-foreground",
246
+ # Submit form when radio button is clicked
247
+ ),
248
+ cls="grid grid-flow-col items-center gap-x-3 border border-input px-3 rounded-sm",
249
+ ),
250
+ Button(
251
+ Lucide(icon="arrow-right", size="21"),
252
+ size="sm",
253
+ type="submit",
254
+ data_button="search-button",
255
+ disabled=True,
256
+ ),
257
+ cls="flex justify-between",
258
+ ),
259
+ check_input_script,
260
+ autocomplete_script,
261
+ submit_form_on_radio_change,
262
+ action=f"/search?query={quote_plus(query_value)}&ranking={quote_plus(ranking_value)}",
263
+ method="GET",
264
+ hx_get="/fetch_results", # As the component is a form, input components query and ranking are sent as query parameters automatically, see https://htmx.org/docs/#parameters
265
+ hx_trigger="load",
266
+ hx_target="#search-results",
267
+ hx_swap="outerHTML",
268
+ hx_indicator="#loading-indicator",
269
+ cls=grid_cls,
270
+ )
271
+
272
+
273
+ def SampleQueries():
274
+ sample_queries = [
275
+ "What percentage of the funds unlisted real estate investments were in Switzerland 2023?",
276
+ "Gender balance at level 4 or above in NY office 2023?",
277
+ "Number of graduate applications trend 2021-2023",
278
+ "Total amount of fixed salaries paid in 2023?",
279
+ "Proportion of female new hires 2021-2023?",
280
+ "child jumping over puddle",
281
+ "hula hoop kid",
282
+ ]
283
+
284
+ query_badges = []
285
+ for query in sample_queries:
286
+ query_badges.append(
287
+ A(
288
+ Badge(
289
+ Div(
290
+ Lucide(
291
+ icon="text-search", size="18", cls="text-muted-foreground"
292
+ ),
293
+ Span(query, cls="text-base font-normal"),
294
+ cls="flex gap-2 items-center",
295
+ ),
296
+ variant="outline",
297
+ cls="text-base font-normal text-muted-foreground hover:border-black dark:hover:border-white",
298
+ ),
299
+ href=f"/search?query={quote_plus(query)}",
300
+ cls="no-underline",
301
+ )
302
+ )
303
+
304
+ return Div(*query_badges, cls="grid gap-2 justify-items-center")
305
+
306
+
307
+ def Hero():
308
+ return Div(
309
+ H1(
310
+ "Visual RAG over PDFs",
311
+ cls="text-5xl md:text-6xl font-bold tracking-wide md:tracking-wider bg-clip-text text-transparent bg-gradient-to-r from-black to-slate-700 dark:from-white dark:to-slate-300 animate-fade-in",
312
+ ),
313
+ P(
314
+ "See how Vespa and ColPali can be used for Visual RAG in this demo",
315
+ cls="text-base md:text-2xl text-muted-foreground md:tracking-wide",
316
+ ),
317
+ cls="grid gap-5 text-center",
318
+ )
319
+
320
+
321
+ def Home():
322
+ return Div(
323
+ Div(
324
+ Hero(),
325
+ SearchBox(with_border=True),
326
+ SampleQueries(),
327
+ ShareButtons(),
328
+ cls="grid gap-8 content-start mt-[13vh]",
329
+ ),
330
+ cls="grid w-full h-full max-w-screen-md gap-4 mx-auto",
331
+ )
332
+
333
+
334
+ def LinkResource(text, href):
335
+ return Li(
336
+ A(
337
+ Lucide(icon="external-link", size="18"),
338
+ text,
339
+ href=href,
340
+ target="_blank",
341
+ cls="flex items-center gap-1.5 hover:underline bold text-md",
342
+ ),
343
+ )
344
+
345
+
346
+ def AboutThisDemo():
347
+ resources = [
348
+ {
349
+ "text": "Vespa Blog: How we built this demo",
350
+ "href": "https://blog.vespa.ai/visual-rag-in-practice",
351
+ },
352
+ {
353
+ "text": "Notebook to set up Vespa application and feed dataset",
354
+ "href": "https://pyvespa.readthedocs.io/en/latest/examples/visual_pdf_rag_with_vespa_colpali_cloud.html",
355
+ },
356
+ {
357
+ "text": "Web App (FastHTML) Code",
358
+ "href": "https://github.com/vespa-engine/sample-apps/tree/master/visual-retrieval-colpali",
359
+ },
360
+ {
361
+ "text": "Vespa Blog: Scaling ColPali to Billions",
362
+ "href": "https://blog.vespa.ai/scaling-colpali-to-billions/",
363
+ },
364
+ {
365
+ "text": "Vespa Blog: Retrieval with Vision Language Models",
366
+ "href": "https://blog.vespa.ai/retrieval-with-vision-language-models-colpali/",
367
+ },
368
+ ]
369
+ return Div(
370
+ H1(
371
+ "About This Demo",
372
+ cls="text-3xl md:text-5xl font-bold tracking-wide md:tracking-wider",
373
+ ),
374
+ P(
375
+ "This demo showcases a Visual Retrieval-Augmented Generation (RAG) application over PDFs using ColPali embeddings in Vespa, built entirely in Python, using FastHTML. The code is fully open source.",
376
+ cls="text-base",
377
+ ),
378
+ Img(
379
+ src="/static/img/colpali_child.png",
380
+ alt="Example of token level similarity map",
381
+ cls="w-full",
382
+ ),
383
+ H2("Resources", cls="text-2xl font-semibold"),
384
+ Ul(
385
+ *[
386
+ LinkResource(resource["text"], resource["href"])
387
+ for resource in resources
388
+ ],
389
+ cls="space-y-2 list-disc pl-5",
390
+ ),
391
+ H2("Architecture Overview", cls="text-2xl font-semibold"),
392
+ Img(
393
+ src="/static/img/visual-retrieval-demoapp-arch.png",
394
+ alt="Architecture Overview",
395
+ cls="w-full",
396
+ ),
397
+ Ul(
398
+ Li(
399
+ Strong("Vespa Application: "),
400
+ "Vespa Application that handles indexing, search, ranking and queries, leveraging features like phased ranking and multivector MaxSim calculations.",
401
+ ),
402
+ Li(
403
+ Strong("Frontend: "),
404
+ "Built with FastHTML, offering a professional and responsive user interface without the complexity of separate frontend frameworks.",
405
+ ),
406
+ Li(
407
+ Strong("Backend: "),
408
+ "Also built with FastHTML. Handles query embedding inference using ColPali, serves static files, and is responsible for orchestrating interactions between Vespa and the frontend.",
409
+ ),
410
+ Li(
411
+ Strong("Gemini API: "),
412
+ "VLM for the AI response, providing responses based on the top results from Vespa.",
413
+ cls="list-disc list-inside",
414
+ ),
415
+ H2("User Experience Highlights", cls="text-2xl font-semibold"),
416
+ Ul(
417
+ Li(
418
+ Strong("Fast and Responsive: "),
419
+ "Optimized for quick loading times, with phased content delivery to display essential information immediately while loading detailed data in the background.",
420
+ ),
421
+ Li(
422
+ Strong("Similarity Maps: "),
423
+ "Provides visual highlights of the most relevant parts of a page in response to a query, enhancing interpretability.",
424
+ ),
425
+ Li(
426
+ Strong("Type-Ahead Suggestions: "),
427
+ "Offers query suggestions to assist users in formulating effective searches.",
428
+ ),
429
+ cls="list-disc list-inside",
430
+ ),
431
+ cls="grid gap-5",
432
+ ),
433
+ H2("Dataset", cls="text-2xl font-semibold"),
434
+ P(
435
+ "The dataset used in this demo is retrieved from reports published by the Norwegian Government Pension Fund Global. It contains 6,992 pages from 116 PDF reports (2000–2024). The information is often presented in visual formats, making it an ideal dataset for visual retrieval applications.",
436
+ cls="text-base",
437
+ ),
438
+ Iframe(
439
+ src="https://huggingface.co/datasets/vespa-engine/gpfg-QA/embed/viewer",
440
+ frameborder="0",
441
+ width="100%",
442
+ height="500",
443
+ ),
444
+ Hr(), # To add some margin to bottom. Probably a much better way to do this, but the mb-[16vh] class doesn't seem to be applied
445
+ cls="w-full h-full max-w-screen-md gap-4 mx-auto mt-[8vh] mb-[16vh] grid gap-8 content-start",
446
+ )
447
+
448
+
449
+ def Search(request, search_results=[]):
450
+ query_value = request.query_params.get("query", "").strip()
451
+ ranking_value = request.query_params.get("ranking", "hybrid")
452
+ return Div(
453
+ Div(
454
+ Div(
455
+ SearchBox(query_value=query_value, ranking_value=ranking_value),
456
+ Div(
457
+ LoadingMessage(),
458
+ id="search-results", # This will be replaced by the search results
459
+ ),
460
+ cls="grid",
461
+ ),
462
+ cls="grid",
463
+ ),
464
+ )
465
+
466
+
467
+ def LoadingMessage(display_text="Retrieving search results"):
468
+ return Div(
469
+ Lucide(icon="loader-circle", cls="size-5 mr-1.5 animate-spin"),
470
+ Span(display_text, cls="text-base text-center"),
471
+ cls="p-10 text-muted-foreground flex items-center justify-center",
472
+ id="loading-indicator",
473
+ )
474
+
475
+
476
+ def LoadingSkeleton():
477
+ return Div(
478
+ Div(cls="h-5 bg-muted"),
479
+ Div(cls="h-5 bg-muted"),
480
+ Div(cls="h-5 bg-muted"),
481
+ cls="grid gap-2 animate-pulse",
482
+ )
483
+
484
+
485
+ def SimMapButtonReady(query_id, idx, token, token_idx, img_src):
486
+ return Button(
487
+ token.replace("\u2581", ""),
488
+ size="sm",
489
+ data_image_src=img_src,
490
+ id=f"sim-map-button-{query_id}-{idx}-{token_idx}-{token}",
491
+ cls="sim-map-button pointer-events-auto font-mono text-xs h-5 rounded-none px-2",
492
+ )
493
+
494
+
495
+ def SimMapButtonPoll(query_id, idx, token, token_idx):
496
+ return Button(
497
+ Lucide(icon="loader-circle", size="15", cls="animate-spin"),
498
+ size="sm",
499
+ disabled=True,
500
+ hx_get=f"/get_sim_map?query_id={query_id}&idx={idx}&token={token}&token_idx={token_idx}",
501
+ hx_trigger="every 0.5s",
502
+ hx_swap="outerHTML",
503
+ cls="pointer-events-auto text-xs h-5 rounded-none px-2",
504
+ )
505
+
506
+
507
+ def SearchInfo(search_time, total_count):
508
+ return (
509
+ Div(
510
+ Span(
511
+ "Retrieved ",
512
+ Strong(total_count),
513
+ Span(" results"),
514
+ Span(" in "),
515
+ Strong(f"{search_time:.3f}"), # 3 significant digits
516
+ Span(" seconds."),
517
+ ),
518
+ cls="grid bg-background border-t text-sm text-center p-3",
519
+ ),
520
+ )
521
+
522
+
523
+ def SearchResult(
524
+ results: list,
525
+ query: str,
526
+ query_id: Optional[str] = None,
527
+ search_time: float = 0,
528
+ total_count: int = 0,
529
+ ):
530
+ if not results:
531
+ return Div(
532
+ P(
533
+ "No results found for your query.",
534
+ cls="text-muted-foreground text-base text-center",
535
+ ),
536
+ cls="grid p-10",
537
+ )
538
+
539
+ doc_ids = []
540
+ # Otherwise, display the search results
541
+ result_items = []
542
+ for idx, result in enumerate(results):
543
+ fields = result["fields"] # Extract the 'fields' part of each result
544
+ doc_id = fields["id"]
545
+ doc_ids.append(doc_id)
546
+ blur_image_base64 = f"data:image/jpeg;base64,{fields['blur_image']}"
547
+
548
+ sim_map_fields = {
549
+ key: value
550
+ for key, value in fields.items()
551
+ if key.startswith(
552
+ "sim_map_"
553
+ ) # filtering is done before creating with 'should_filter_token'-function
554
+ }
555
+
556
+ # Generate buttons for the sim_map fields
557
+ sim_map_buttons = []
558
+ for key, value in sim_map_fields.items():
559
+ token = key.split("_")[-2]
560
+ token_idx = int(key.split("_")[-1])
561
+ if value is not None:
562
+ sim_map_base64 = f"data:image/jpeg;base64,{value}"
563
+ sim_map_buttons.append(
564
+ SimMapButtonReady(
565
+ query_id=query_id,
566
+ idx=idx,
567
+ token=token,
568
+ token_idx=token_idx,
569
+ img_src=sim_map_base64,
570
+ )
571
+ )
572
+ else:
573
+ sim_map_buttons.append(
574
+ SimMapButtonPoll(
575
+ query_id=query_id,
576
+ idx=idx,
577
+ token=token,
578
+ token_idx=token_idx,
579
+ )
580
+ )
581
+
582
+ # Add "Reset Image" button to restore the full image
583
+ reset_button = Button(
584
+ "Reset",
585
+ variant="outline",
586
+ size="sm",
587
+ data_image_src=blur_image_base64,
588
+ cls="reset-button pointer-events-auto font-mono text-xs h-5 rounded-none px-2",
589
+ )
590
+
591
+ tokens_icon = Lucide(icon="images", size="15")
592
+
593
+ # Add "Tokens" button - this has no action, just a placeholder
594
+ tokens_button = Button(
595
+ tokens_icon,
596
+ "Tokens",
597
+ size="sm",
598
+ cls="tokens-button flex gap-[3px] font-bold pointer-events-none font-mono text-xs h-5 rounded-none px-2",
599
+ )
600
+
601
+ result_items.append(
602
+ Div(
603
+ Div(
604
+ Div(
605
+ Lucide(icon="file-text"),
606
+ H2(fields["title"], cls="text-xl md:text-2xl font-semibold"),
607
+ Separator(orientation="vertical"),
608
+ Badge(
609
+ f"Relevance score: {result['relevance']:.4f}",
610
+ cls="flex gap-1.5 items-center justify-center",
611
+ ),
612
+ cls="flex items-center gap-2",
613
+ ),
614
+ Div(
615
+ Button(
616
+ "Hide Text",
617
+ size="sm",
618
+ id=f"toggle-button-{idx}",
619
+ onclick=f"toggleTextContent({idx})",
620
+ cls="hidden md:block",
621
+ ),
622
+ ),
623
+ cls="flex flex-wrap items-center justify-between bg-background px-3 py-4",
624
+ ),
625
+ Div(
626
+ Div(
627
+ Div(
628
+ tokens_button,
629
+ *sim_map_buttons,
630
+ reset_button,
631
+ cls="flex flex-wrap gap-px w-full pointer-events-none",
632
+ ),
633
+ Div(
634
+ Div(
635
+ Div(
636
+ Img(
637
+ src=blur_image_base64,
638
+ hx_get=f"/full_image?doc_id={doc_id}",
639
+ style="backdrop-filter: blur(5px);",
640
+ hx_trigger="load",
641
+ hx_swap="outerHTML",
642
+ alt=fields["title"],
643
+ cls="result-image w-full h-full object-contain",
644
+ ),
645
+ Div(
646
+ cls="overlay-container absolute top-0 left-0 w-full h-full pointer-events-none"
647
+ ),
648
+ cls="relative w-full h-full",
649
+ ),
650
+ cls="grid bg-muted p-2",
651
+ ),
652
+ cls="block",
653
+ ),
654
+ id=f"image-column-{idx}",
655
+ cls="image-column relative bg-background px-3 py-5 grid-image-column",
656
+ ),
657
+ Div(
658
+ Div(
659
+ A(
660
+ Lucide(icon="external-link", size="18"),
661
+ f"PDF Source (Page {fields['page_number'] + 1})",
662
+ href=f"{fields['url']}#page={fields['page_number'] + 1}",
663
+ target="_blank",
664
+ cls="flex items-center gap-1.5 font-mono bold text-sm",
665
+ ),
666
+ cls="flex items-center justify-end",
667
+ ),
668
+ Div(
669
+ Div(
670
+ Div(
671
+ Div(
672
+ Div(
673
+ H3(
674
+ "Dynamic summary",
675
+ cls="text-base font-semibold",
676
+ ),
677
+ P(
678
+ NotStr(fields.get("snippet", "")),
679
+ cls="text-highlight text-muted-foreground",
680
+ ),
681
+ cls="grid grid-rows-[auto_0px] content-start gap-y-3",
682
+ ),
683
+ id=f"result-text-snippet-{idx}",
684
+ cls="grid gap-y-3 p-8 border border-dashed",
685
+ ),
686
+ Div(
687
+ Div(
688
+ Div(
689
+ H3(
690
+ "Full text",
691
+ cls="text-base font-semibold",
692
+ ),
693
+ Div(
694
+ P(
695
+ NotStr(fields.get("text", "")),
696
+ cls="text-highlight text-muted-foreground",
697
+ ),
698
+ Br(),
699
+ ),
700
+ cls="grid grid-rows-[auto_0px] content-start gap-y-3",
701
+ ),
702
+ id=f"result-text-full-{idx}",
703
+ cls="grid gap-y-3 p-8 border border-dashed",
704
+ ),
705
+ Div(
706
+ cls="absolute inset-x-0 bottom-0 bg-gradient-to-t from-[#fcfcfd] dark:from-[#1c2024] pt-[7%]"
707
+ ),
708
+ cls="relative grid",
709
+ ),
710
+ cls="grid grid-rows-[1fr_1fr] xl:grid-rows-[1fr_2fr] gap-y-8 p-8 text-sm",
711
+ ),
712
+ cls="grid bg-background",
713
+ ),
714
+ cls="grid bg-muted p-2",
715
+ ),
716
+ id=f"text-column-{idx}",
717
+ cls="text-column relative bg-background px-3 py-5 hidden md-grid-text-column",
718
+ ),
719
+ id=f"image-text-columns-{idx}",
720
+ cls="relative grid grid-cols-1 border-t grid-image-text-columns",
721
+ ),
722
+ cls="grid grid-cols-1 grid-rows-[auto_auto_1fr]",
723
+ ),
724
+ )
725
+
726
+ return [
727
+ Div(
728
+ SearchInfo(search_time, total_count),
729
+ *result_items,
730
+ image_swapping,
731
+ toggle_text_content,
732
+ dynamic_elements_scrollbars,
733
+ id="search-results",
734
+ cls="grid grid-cols-1 gap-px bg-border min-h-0",
735
+ ),
736
+ Div(
737
+ ChatResult(query_id=query_id, query=query, doc_ids=doc_ids),
738
+ hx_swap_oob="true",
739
+ id="chat_messages",
740
+ ),
741
+ ]
742
+
743
+
744
+ def ChatResult(query_id: str, query: str, doc_ids: Optional[list] = None):
745
+ messages = Div(LoadingSkeleton())
746
+
747
+ if doc_ids:
748
+ messages = Div(
749
+ LoadingSkeleton(),
750
+ hx_ext="sse",
751
+ sse_connect=f"/get-message?query_id={query_id}&doc_ids={','.join(doc_ids)}&query={quote_plus(query)}",
752
+ sse_swap="message",
753
+ sse_close="close",
754
+ hx_swap="innerHTML",
755
+ )
756
+
757
+ return Div(
758
+ Div("AI-response (Gemini-8B)", cls="text-xl font-semibold p-5"),
759
+ Div(
760
+ Div(
761
+ messages,
762
+ ),
763
+ id="chat-messages",
764
+ cls="overflow-auto min-h-0 grid items-end px-5",
765
+ ),
766
+ id="chat_messages",
767
+ cls="h-full grid grid-rows-[auto_1fr_auto] min-h-0 gap-3",
768
+ )
frontend/layout.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml.components import Body, Div, Header, Img, Nav, Title
2
+ from fasthtml.xtend import A, Script
3
+ from lucide_fasthtml import Lucide
4
+ from shad4fast import Button, Separator
5
+
6
+ layout_script = Script(
7
+ """
8
+ document.addEventListener("DOMContentLoaded", function () {
9
+ const main = document.querySelector('main');
10
+ const aside = document.querySelector('aside');
11
+ const body = document.body;
12
+
13
+ if (main && aside && main.nextElementSibling === aside) {
14
+ // If we have both main and aside, adjust the layout for larger screens
15
+ body.classList.remove('grid-cols-1'); // Remove single-column layout
16
+ body.classList.add('md:grid-cols-[minmax(0,_45fr)_minmax(0,_15fr)]'); // Two-column layout on larger screens
17
+ } else if (main) {
18
+ // If only main, keep it full width
19
+ body.classList.add('grid-cols-1');
20
+ }
21
+ });
22
+ """
23
+ )
24
+
25
+ overlay_scrollbars_manager = Script(
26
+ """
27
+ (function () {
28
+ const { OverlayScrollbars } = OverlayScrollbarsGlobal;
29
+
30
+ function getPreferredTheme() {
31
+ return localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
32
+ ? 'dark'
33
+ : 'light';
34
+ }
35
+
36
+ function applyOverlayScrollbars(element, scrollbarTheme) {
37
+ // Destroy existing OverlayScrollbars instance if it exists
38
+ const instance = OverlayScrollbars(element);
39
+ if (instance) {
40
+ instance.destroy();
41
+ }
42
+
43
+ // Reinitialize OverlayScrollbars with the correct theme and settings
44
+ OverlayScrollbars(element, {
45
+ overflow: {
46
+ x: 'hidden',
47
+ y: 'scroll'
48
+ },
49
+ scrollbars: {
50
+ theme: scrollbarTheme,
51
+ visibility: 'auto',
52
+ autoHide: 'leave',
53
+ autoHideDelay: 800
54
+ }
55
+ });
56
+ }
57
+
58
+ // Function to get the current scrollbar theme (light or dark)
59
+ function getScrollbarTheme() {
60
+ const isDarkMode = getPreferredTheme() === 'dark';
61
+ return isDarkMode ? 'os-theme-light' : 'os-theme-dark'; // Light theme in dark mode, dark theme in light mode
62
+ }
63
+
64
+ // Expose the common functions globally for reuse
65
+ window.OverlayScrollbarsManager = {
66
+ applyOverlayScrollbars: applyOverlayScrollbars,
67
+ getScrollbarTheme: getScrollbarTheme
68
+ };
69
+ })();
70
+ """
71
+ )
72
+
73
+ static_elements_scrollbars = Script(
74
+ """
75
+ (function () {
76
+ const { applyOverlayScrollbars, getScrollbarTheme } = OverlayScrollbarsManager;
77
+
78
+ function applyScrollbarsToStaticElements() {
79
+ const mainElement = document.querySelector('main');
80
+ const chatMessagesElement = document.querySelector('#chat-messages');
81
+
82
+ const scrollbarTheme = getScrollbarTheme();
83
+
84
+ if (mainElement) {
85
+ applyOverlayScrollbars(mainElement, scrollbarTheme);
86
+ }
87
+
88
+ if (chatMessagesElement) {
89
+ applyOverlayScrollbars(chatMessagesElement, scrollbarTheme);
90
+ }
91
+ }
92
+
93
+ // Apply the scrollbars on page load
94
+ applyScrollbarsToStaticElements();
95
+
96
+ // Observe changes in the 'dark' class on the <html> element to adjust the theme dynamically
97
+ const observer = new MutationObserver(applyScrollbarsToStaticElements);
98
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
99
+ })();
100
+ """
101
+ )
102
+
103
+
104
+ def Logo():
105
+ return Div(
106
+ Img(
107
+ src="https://assets.vespa.ai/logos/vespa-logo-black.svg",
108
+ alt="Vespa Logo",
109
+ cls="h-full dark:hidden",
110
+ ),
111
+ Img(
112
+ src="https://assets.vespa.ai/logos/vespa-logo-white.svg",
113
+ alt="Vespa Logo Dark Mode",
114
+ cls="h-full hidden dark:block",
115
+ ),
116
+ cls="h-[27px]",
117
+ )
118
+
119
+
120
+ def ThemeToggle(variant="ghost", cls=None, **kwargs):
121
+ return Button(
122
+ Lucide("sun", cls="dark:flex hidden"),
123
+ Lucide("moon", cls="dark:hidden"),
124
+ variant=variant,
125
+ size="icon",
126
+ cls=f"theme-toggle {cls}",
127
+ **kwargs,
128
+ )
129
+
130
+
131
+ def Links():
132
+ return Nav(
133
+ A(
134
+ Button("About this demo?", variant="link"),
135
+ href="/about-this-demo",
136
+ ),
137
+ Separator(orientation="vertical"),
138
+ A(
139
+ Button(Lucide(icon="github"), size="icon", variant="ghost"),
140
+ href="https://github.com/vespa-engine/vespa",
141
+ target="_blank",
142
+ ),
143
+ A(
144
+ Button(Lucide(icon="slack"), size="icon", variant="ghost"),
145
+ href="https://slack.vespa.ai",
146
+ target="_blank",
147
+ ),
148
+ Separator(orientation="vertical"),
149
+ ThemeToggle(),
150
+ cls="flex items-center space-x-2",
151
+ )
152
+
153
+
154
+ def Layout(*c, is_home=False, **kwargs):
155
+ return (
156
+ Title("Visual Retrieval ColPali"),
157
+ Body(
158
+ Header(
159
+ A(Logo(), href="/"),
160
+ Links(),
161
+ cls="min-h-[55px] h-[55px] w-full flex items-center justify-between px-4",
162
+ ),
163
+ *c,
164
+ **kwargs,
165
+ data_is_home=str(is_home).lower(),
166
+ cls="grid grid-rows-[minmax(0,55px)_minmax(0,1fr)] min-h-0",
167
+ ),
168
+ layout_script,
169
+ overlay_scrollbars_manager,
170
+ static_elements_scrollbars,
171
+ )
globals.css ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+
6
+ @layer base {
7
+ :root {
8
+ --background: 240 20% 99%; /* 1 */
9
+ --foreground: 210 13% 13%; /* 12 */
10
+ --card: 240 20% 99%; /* 1 */
11
+ --card-foreground: 210 13% 13%; /* 12 */
12
+ --popover: 240 20% 99%; /* 1 */
13
+ --popover-foreground: 210 13% 13%; /* 12 */
14
+ --primary: 210 13% 13%; /* 12 */
15
+ --primary-foreground: 240 20% 98%; /* 2 */
16
+ --secondary: 240 11% 95%; /* 3 */
17
+ --secondary-foreground: 210 13% 13%; /* 12 */
18
+ --muted: 240 11% 95%; /* 3 */
19
+ --muted-foreground: 220 6% 40%; /* 11 */
20
+ --accent: 240 11% 95%; /* 3 */
21
+ --accent-foreground: 210 13% 13%; /* 12 */
22
+ --destructive: 358 75% 59%; /* 9 - red */
23
+ --destructive-foreground: 240 20% 98%; /* 2 */
24
+ --border: 240 10% 86%; /* 6 */
25
+ --input: 240 10% 86%; /* 6 */
26
+ --ring: 210 13% 13%; /* 12 */
27
+ --chart-1: 10 78% 54%; /* 9 - tomato */
28
+ --chart-2: 173 80% 36%; /* 9 - teal */
29
+ --chart-3: 206 100% 50%; /* 9 - blue */
30
+ --chart-4: 42 100% 62%; /* 9 - amber */
31
+ --chart-5: 23 93% 53%; /* 9 - orange */
32
+ }
33
+
34
+ .dark {
35
+ --background: 240 6% 7%; /* 1 */
36
+ --foreground: 220 9% 94%; /* 12 */
37
+ --card: 240 6% 7%; /* 1 */
38
+ --card-foreground: 220 9% 94%; /* 12 */
39
+ --popover: 240 6% 7%; /* 1 */
40
+ --popover-foreground: 220 9% 94%; /* 12 */
41
+ --primary: 220 9% 94%; /* 12 */
42
+ --primary-foreground: 220 6% 10%; /* 2 */
43
+ --secondary: 225 6% 14%; /* 3 */
44
+ --secondary-foreground: 220 9% 94%; /* 12 */
45
+ --muted: 225 6% 14%; /* 3 */
46
+ --muted-foreground: 216 7% 71%; /* 11 */
47
+ --accent: 225 6% 14%; /* 3 */
48
+ --accent-foreground: 220 9% 94%; /* 12 */
49
+ --destructive: 358 75% 59%; /* 9 - red */
50
+ --destructive-foreground: 220 9% 94%; /* 12 */
51
+ --border: 213 8% 23%; /* 6 */
52
+ --input: 213 8% 23%; /* 6 */
53
+ --ring: 220 9% 94%; /* 12 */
54
+ --chart-1: 10 78% 54%; /* 9 - tomato */
55
+ --chart-2: 173 80% 36%; /* 9 - teal */
56
+ --chart-3: 206 100% 50%; /* 9 - blue */
57
+ --chart-4: 42 100% 62%; /* 9 - amber */
58
+ --chart-5: 23 93% 53%; /* 9 - orange */
59
+ }
60
+ }
61
+
62
+ @layer base {
63
+ :root:has(.no-bg-scroll) {
64
+ overflow: hidden;
65
+ }
66
+
67
+ * {
68
+ @apply border-border;
69
+ }
70
+
71
+ body {
72
+ @apply bg-background text-foreground antialiased min-h-screen;
73
+ font-feature-settings: "rlig" 1, "calt" 1;
74
+ }
75
+ }
76
+
77
+ @layer utilities {
78
+
79
+ /* Hide scrollbar for Chrome, Safari and Opera */
80
+ .no-scrollbar::-webkit-scrollbar {
81
+ display: none;
82
+ }
83
+
84
+ /* Hide scrollbar for IE, Edge and Firefox */
85
+ .no-scrollbar {
86
+ -webkit-overflow-scrolling: touch;
87
+ -ms-overflow-style: none;
88
+ /* IE and Edge */
89
+ scrollbar-width: none;
90
+ /* Firefox */
91
+ }
92
+ }
93
+
94
+ @keyframes slideInFromTop {
95
+ from {
96
+ transform: translateY(-100%);
97
+ }
98
+
99
+ to {
100
+ transform: translateY(0);
101
+ }
102
+ }
103
+
104
+ @keyframes slideInFromBottom {
105
+ from {
106
+ transform: translateY(100%);
107
+ }
108
+
109
+ to {
110
+ transform: translateY(0);
111
+ }
112
+ }
113
+
114
+ .toast {
115
+ animation-duration: 0.2s;
116
+ animation-fill-mode: forwards;
117
+ }
118
+
119
+ @media (max-width: 640px) {
120
+ .toast {
121
+ animation-name: slideInFromTop;
122
+ }
123
+ }
124
+
125
+ @media (min-width: 641px) {
126
+ .toast {
127
+ animation-name: slideInFromBottom;
128
+ }
129
+ }
130
+
131
+ @keyframes fade-in {
132
+ from {
133
+ opacity: 0;
134
+ }
135
+ to {
136
+ opacity: 1;
137
+ }
138
+ }
139
+
140
+ @keyframes slide-up {
141
+ from {
142
+ transform: translateY(20px);
143
+ opacity: 0;
144
+ }
145
+ to {
146
+ transform: translateY(0);
147
+ opacity: 1;
148
+ }
149
+ }
150
+
151
+ .animate-fade-in {
152
+ animation: fade-in 1s ease-out forwards;
153
+ }
154
+
155
+ .animate-slide-up {
156
+ animation: slide-up 1s ease-out forwards;
157
+ }
158
+
159
+ .sim-map-button.active {
160
+ background-color: #61D790;
161
+ color: #2E2F27;
162
+
163
+ &:hover {
164
+ background-color: #61D790;
165
+ }
166
+ }
167
+
168
+ .text-highlight strong {
169
+ color: black;
170
+
171
+ .dark & {
172
+ color: white;
173
+ }
174
+ }
175
+
176
+ .tokens-button {
177
+ background-color: #B7E2F1;
178
+ color: #2E2F27;
179
+ }
180
+
181
+ .overlay-image {
182
+ opacity: 0.5;
183
+ position: absolute;
184
+ top: 0;
185
+ left: 0;
186
+ width: 100%;
187
+ height: 100%;
188
+ z-index: 10;
189
+ }
190
+
191
+ header {
192
+ grid-column: 1/-1;
193
+ }
194
+
195
+ body {
196
+ &[data-is-home="true"] {
197
+ background: radial-gradient(circle at 50% 100%, #fcfcfd, #fcfcfd, #fdfdfe, #fdfdfe, #fefefe, #fefefe, #ffffff, #ffffff);
198
+
199
+ .dark & {
200
+ background: radial-gradient(circle at 50% 50%, #272a2d, #242629, #212326, #1e1f22, #1b1c1e, #18181b, #151517, #111113);
201
+ }
202
+ }
203
+ }
204
+
205
+ main {
206
+ overflow: auto;
207
+ }
208
+
209
+ aside {
210
+ overflow: auto;
211
+ }
212
+
213
+ .scroll-container {
214
+ padding-right: 10px;
215
+ }
216
+
217
+ .question-message {
218
+ background-color: #61D790;
219
+ color: #2E2F27;
220
+ }
221
+
222
+ .grid-image-text-columns {
223
+ @apply md:grid-cols-2 md:col-span-2
224
+ }
225
+
226
+ .grid-image-column {
227
+ @apply grid grid-rows-subgrid row-span-2 content-start;
228
+ }
229
+
230
+ .md-grid-text-column {
231
+ @apply md:grid md:grid-rows-subgrid md:row-span-2 md:content-start;
232
+ }
233
+
234
+ #search-input[aria-expanded="true"] {
235
+ border-top: 1px solid hsl(var(--input));
236
+ border-left: 1px solid hsl(var(--input));
237
+ border-right: 1px solid hsl(var(--input));
238
+ border-bottom: none;
239
+ border-bottom-left-radius: 0;
240
+ border-bottom-right-radius: 0;
241
+ }
242
+
243
+ .awesomplete {
244
+ width: 100%;
245
+ }
246
+
247
+ .awesomplete > ul {
248
+ @apply text-sm space-y-1;
249
+ margin: 0;
250
+ border-top: none;
251
+ border-left: 1px solid hsl(var(--input));
252
+ border-right: 1px solid hsl(var(--input));
253
+ border-bottom: 1px solid hsl(var(--input));
254
+ border-radius: 0 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px);
255
+ background: white;
256
+
257
+ .dark & {
258
+ background: hsl(var(--background));
259
+ }
260
+
261
+ box-shadow: none;
262
+ text-shadow: none;
263
+ }
264
+
265
+ .awesomplete > ul:before {
266
+ display: none;
267
+ }
268
+
269
+ .awesomplete > ul > li:hover {
270
+ background-color: #B7E2F1;
271
+ color: #2E2F27;
272
+ }
273
+
274
+ .awesomplete > ul > li[aria-selected="true"] {
275
+ background-color: #B7E2F1;
276
+ color: #2E2F27;
277
+ }
278
+
279
+ .awesomplete mark {
280
+ background-color: #61D790;
281
+ color: #2E2F27;
282
+ }
283
+
284
+ .awesomplete li:hover mark {
285
+ background-color: #61D790;
286
+ color: #2E2F27;
287
+ }
288
+
289
+ .awesomplete li[aria-selected="true"] mark {
290
+ background-color: #61D790;
291
+ color: #2E2F27;
292
+ }
293
+
icons.py ADDED
@@ -0,0 +1 @@
 
 
1
+ ICONS = {"chevrons-right": "<path d=\"m6 17 5-5-5-5\"></path><path d=\"m13 17 5-5-5-5\"></path>", "moon": "<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"></path>", "sun": "<circle cx=\"12\" cy=\"12\" r=\"4\"></circle><path d=\"M12 2v2\"></path><path d=\"M12 20v2\"></path><path d=\"m4.93 4.93 1.41 1.41\"></path><path d=\"m17.66 17.66 1.41 1.41\"></path><path d=\"M2 12h2\"></path><path d=\"M20 12h2\"></path><path d=\"m6.34 17.66-1.41 1.41\"></path><path d=\"m19.07 4.93-1.41 1.41\"></path>", "github": "<path d=\"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4\"></path><path d=\"M9 18c-4.51 2-5-2-7-2\"></path>", "slack": "<rect height=\"8\" rx=\"1.5\" width=\"3\" x=\"13\" y=\"2\"></rect><path d=\"M19 8.5V10h1.5A1.5 1.5 0 1 0 19 8.5\"></path><rect height=\"8\" rx=\"1.5\" width=\"3\" x=\"8\" y=\"14\"></rect><path d=\"M5 15.5V14H3.5A1.5 1.5 0 1 0 5 15.5\"></path><rect height=\"3\" rx=\"1.5\" width=\"8\" x=\"14\" y=\"13\"></rect><path d=\"M15.5 19H14v1.5a1.5 1.5 0 1 0 1.5-1.5\"></path><rect height=\"3\" rx=\"1.5\" width=\"8\" x=\"2\" y=\"8\"></rect><path d=\"M8.5 5H10V3.5A1.5 1.5 0 1 0 8.5 5\"></path>", "settings": "<path d=\"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z\"></path><circle cx=\"12\" cy=\"12\" r=\"3\"></circle>", "arrow-right": "<path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path>", "search": "<circle cx=\"11\" cy=\"11\" r=\"8\"></circle><path d=\"m21 21-4.3-4.3\"></path>", "file-search": "<path d=\"M14 2v4a2 2 0 0 0 2 2h4\"></path><path d=\"M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3\"></path><path d=\"m9 18-1.5-1.5\"></path><circle cx=\"5\" cy=\"14\" r=\"3\"></circle>", "message-circle-question": "<path d=\"M7.9 20A9 9 0 1 0 4 16.1L2 22Z\"></path><path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"></path><path d=\"M12 17h.01\"></path>", "text-search": "<path d=\"M21 6H3\"></path><path d=\"M10 12H3\"></path><path d=\"M10 18H3\"></path><circle cx=\"17\" cy=\"15\" r=\"3\"></circle><path d=\"m21 19-1.9-1.9\"></path>", "maximize": "<path d=\"M8 3H5a2 2 0 0 0-2 2v3\"></path><path d=\"M21 8V5a2 2 0 0 0-2-2h-3\"></path><path d=\"M3 16v3a2 2 0 0 0 2 2h3\"></path><path d=\"M16 21h3a2 2 0 0 0 2-2v-3\"></path>", "expand": "<path d=\"m21 21-6-6m6 6v-4.8m0 4.8h-4.8\"></path><path d=\"M3 16.2V21m0 0h4.8M3 21l6-6\"></path><path d=\"M21 7.8V3m0 0h-4.8M21 3l-6 6\"></path><path d=\"M3 7.8V3m0 0h4.8M3 3l6 6\"></path>", "fullscreen": "<path d=\"M3 7V5a2 2 0 0 1 2-2h2\"></path><path d=\"M17 3h2a2 2 0 0 1 2 2v2\"></path><path d=\"M21 17v2a2 2 0 0 1-2 2h-2\"></path><path d=\"M7 21H5a2 2 0 0 1-2-2v-2\"></path><rect height=\"8\" rx=\"1\" width=\"10\" x=\"7\" y=\"8\"></rect>", "images": "<path d=\"M18 22H4a2 2 0 0 1-2-2V6\"></path><path d=\"m22 13-1.296-1.296a2.41 2.41 0 0 0-3.408 0L11 18\"></path><circle cx=\"12\" cy=\"8\" r=\"2\"></circle><rect height=\"16\" rx=\"2\" width=\"16\" x=\"6\" y=\"2\"></rect>", "circle": "<circle cx=\"12\" cy=\"12\" r=\"10\"></circle>", "loader-circle": "<path d=\"M21 12a9 9 0 1 1-6.219-8.56\"></path>", "file-text": "<path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\"></path><path d=\"M14 2v4a2 2 0 0 0 2 2h4\"></path><path d=\"M10 9H8\"></path><path d=\"M16 13H8\"></path><path d=\"M16 17H8\"></path>", "file-question": "<path d=\"M12 17h.01\"></path><path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z\"></path><path d=\"M9.1 9a3 3 0 0 1 5.82 1c0 2-3 3-3 3\"></path>", "external-link": "<path d=\"M15 3h6v6\"></path><path d=\"M10 14 21 3\"></path><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"></path>", "linkedin": "<path d=\"M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z\"></path><rect height=\"12\" width=\"4\" x=\"2\" y=\"9\"></rect><circle cx=\"4\" cy=\"4\" r=\"2\"></circle>"}
main.py ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import base64
3
+ import os
4
+ import time
5
+ import uuid
6
+ import logging
7
+ import sys
8
+ from concurrent.futures import ThreadPoolExecutor
9
+ from pathlib import Path
10
+
11
+ import google.generativeai as genai
12
+ from fastcore.parallel import threaded
13
+ from fasthtml.common import (
14
+ Aside,
15
+ Div,
16
+ FileResponse,
17
+ HighlightJS,
18
+ Img,
19
+ JSONResponse,
20
+ Link,
21
+ Main,
22
+ P,
23
+ RedirectResponse,
24
+ Script,
25
+ StreamingResponse,
26
+ fast_app,
27
+ serve,
28
+ )
29
+ from PIL import Image
30
+ from shad4fast import ShadHead
31
+ from vespa.application import Vespa
32
+
33
+ from backend.colpali import SimMapGenerator
34
+ from backend.vespa_app import VespaQueryClient
35
+ from frontend.app import (
36
+ AboutThisDemo,
37
+ ChatResult,
38
+ Home,
39
+ Search,
40
+ SearchBox,
41
+ SearchResult,
42
+ SimMapButtonPoll,
43
+ SimMapButtonReady,
44
+ )
45
+ from frontend.layout import Layout
46
+
47
+ highlight_js_theme_link = Link(id="highlight-theme", rel="stylesheet", href="")
48
+ highlight_js_theme = Script(src="/static/js/highlightjs-theme.js")
49
+ highlight_js = HighlightJS(
50
+ langs=["python", "javascript", "java", "json", "xml"],
51
+ dark="github-dark",
52
+ light="github",
53
+ )
54
+
55
+ overlayscrollbars_link = Link(
56
+ rel="stylesheet",
57
+ href="https://cdnjs.cloudflare.com/ajax/libs/overlayscrollbars/2.10.0/styles/overlayscrollbars.min.css",
58
+ type="text/css",
59
+ )
60
+ overlayscrollbars_js = Script(
61
+ src="https://cdnjs.cloudflare.com/ajax/libs/overlayscrollbars/2.10.0/browser/overlayscrollbars.browser.es5.min.js"
62
+ )
63
+ awesomplete_link = Link(
64
+ rel="stylesheet",
65
+ href="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.7/awesomplete.min.css",
66
+ type="text/css",
67
+ )
68
+ awesomplete_js = Script(
69
+ src="https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.7/awesomplete.min.js"
70
+ )
71
+ sselink = Script(src="https://unpkg.com/[email protected]/sse.js")
72
+
73
+ # Get log level from environment variable, default to INFO
74
+ LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()
75
+ # Configure logger
76
+ logger = logging.getLogger("vespa_app")
77
+ handler = logging.StreamHandler(sys.stdout)
78
+ handler.setFormatter(
79
+ logging.Formatter(
80
+ "%(levelname)s: \t %(asctime)s \t %(message)s",
81
+ datefmt="%Y-%m-%d %H:%M:%S",
82
+ )
83
+ )
84
+ logger.addHandler(handler)
85
+ logger.setLevel(getattr(logging, LOG_LEVEL))
86
+
87
+ app, rt = fast_app(
88
+ htmlkw={"cls": "grid h-full"},
89
+ pico=False,
90
+ hdrs=(
91
+ highlight_js,
92
+ highlight_js_theme_link,
93
+ highlight_js_theme,
94
+ overlayscrollbars_link,
95
+ overlayscrollbars_js,
96
+ awesomplete_link,
97
+ awesomplete_js,
98
+ sselink,
99
+ ShadHead(tw_cdn=False, theme_handle=True),
100
+ ),
101
+ )
102
+ vespa_app: Vespa = VespaQueryClient(logger=logger)
103
+ thread_pool = ThreadPoolExecutor()
104
+ # Gemini config
105
+
106
+ genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
107
+ GEMINI_SYSTEM_PROMPT = """If the user query is a question, try your best to answer it based on the provided images.
108
+ If the user query can not be interpreted as a question, or if the answer to the query can not be inferred from the images,
109
+ answer with the exact phrase "I am sorry, I can't find enough relevant information on these pages to answer your question.".
110
+ Your response should be HTML formatted, but only simple tags, such as <b>. <p>, <i>, <br> <ul> and <li> are allowed. No HTML tables.
111
+ This means that newlines will be replaced with <br> tags, bold text will be enclosed in <b> tags, and so on.
112
+ Do NOT include backticks (`) in your response. Only simple HTML tags and text.
113
+ """
114
+ gemini_model = genai.GenerativeModel(
115
+ "gemini-1.5-flash-8b", system_instruction=GEMINI_SYSTEM_PROMPT
116
+ )
117
+ STATIC_DIR = Path("static")
118
+ IMG_DIR = STATIC_DIR / "full_images"
119
+ SIM_MAP_DIR = STATIC_DIR / "sim_maps"
120
+ os.makedirs(IMG_DIR, exist_ok=True)
121
+ os.makedirs(SIM_MAP_DIR, exist_ok=True)
122
+
123
+
124
+ @app.on_event("startup")
125
+ def load_model_on_startup():
126
+ app.sim_map_generator = SimMapGenerator(logger=logger)
127
+ return
128
+
129
+
130
+ @app.on_event("startup")
131
+ async def keepalive():
132
+ asyncio.create_task(poll_vespa_keepalive())
133
+ return
134
+
135
+
136
+ def generate_query_id(query, ranking_value):
137
+ hash_input = (query + ranking_value).encode("utf-8")
138
+ return hash(hash_input)
139
+
140
+
141
+ @rt("/static/{filepath:path}")
142
+ def serve_static(filepath: str):
143
+ return FileResponse(STATIC_DIR / filepath)
144
+
145
+
146
+ @rt("/")
147
+ def get(session):
148
+ if "session_id" not in session:
149
+ session["session_id"] = str(uuid.uuid4())
150
+ return Layout(Main(Home()), is_home=True)
151
+
152
+
153
+ @rt("/about-this-demo")
154
+ def get():
155
+ return Layout(Main(AboutThisDemo()))
156
+
157
+
158
+ @rt("/search")
159
+ def get(request, query: str = "", ranking: str = "hybrid"):
160
+ logger.info(f"/search: Fetching results for query: {query}, ranking: {ranking}")
161
+
162
+ # Always render the SearchBox first
163
+ if not query:
164
+ # Show SearchBox and a message for missing query
165
+ return Layout(
166
+ Main(
167
+ Div(
168
+ SearchBox(query_value=query, ranking_value=ranking),
169
+ Div(
170
+ P(
171
+ "No query provided. Please enter a query.",
172
+ cls="text-center text-muted-foreground",
173
+ ),
174
+ cls="p-10",
175
+ ),
176
+ cls="grid",
177
+ )
178
+ )
179
+ )
180
+ # Generate a unique query_id based on the query and ranking value
181
+ query_id = generate_query_id(query, ranking)
182
+ # Show the loading message if a query is provided
183
+ return Layout(
184
+ Main(Search(request), data_overlayscrollbars_initialize=True, cls="border-t"),
185
+ Aside(
186
+ ChatResult(query_id=query_id, query=query),
187
+ cls="border-t border-l hidden md:block",
188
+ ),
189
+ ) # Show SearchBox and Loading message initially
190
+
191
+
192
+ @rt("/fetch_results")
193
+ async def get(session, request, query: str, ranking: str):
194
+ if "hx-request" not in request.headers:
195
+ return RedirectResponse("/search")
196
+
197
+ # Get the hash of the query and ranking value
198
+ query_id = generate_query_id(query, ranking)
199
+ logger.info(f"Query id in /fetch_results: {query_id}")
200
+ # Run the embedding and query against Vespa app
201
+ start_inference = time.perf_counter()
202
+ q_embs, idx_to_token = app.sim_map_generator.get_query_embeddings_and_token_map(
203
+ query
204
+ )
205
+ end_inference = time.perf_counter()
206
+ logger.info(
207
+ f"Inference time for query_id: {query_id} \t {end_inference - start_inference:.2f} seconds"
208
+ )
209
+
210
+ start = time.perf_counter()
211
+ # Fetch real search results from Vespa
212
+ result = await vespa_app.get_result_from_query(
213
+ query=query,
214
+ q_embs=q_embs,
215
+ ranking=ranking,
216
+ idx_to_token=idx_to_token,
217
+ )
218
+ end = time.perf_counter()
219
+ logger.info(
220
+ f"Search results fetched in {end - start:.2f} seconds. Vespa search time: {result['timing']['searchtime']}"
221
+ )
222
+ search_time = result["timing"]["searchtime"]
223
+ # Safely get total_count with a default of 0
224
+ total_count = result.get("root", {}).get("fields", {}).get("totalCount", 0)
225
+
226
+ search_results = vespa_app.results_to_search_results(result, idx_to_token)
227
+
228
+ get_and_store_sim_maps(
229
+ query_id=query_id,
230
+ query=query,
231
+ q_embs=q_embs,
232
+ ranking=ranking,
233
+ idx_to_token=idx_to_token,
234
+ doc_ids=[result["fields"]["id"] for result in search_results],
235
+ )
236
+ return SearchResult(search_results, query, query_id, search_time, total_count)
237
+
238
+
239
+ def get_results_children(result):
240
+ search_results = (
241
+ result["root"]["children"]
242
+ if "root" in result and "children" in result["root"]
243
+ else []
244
+ )
245
+ return search_results
246
+
247
+
248
+ async def poll_vespa_keepalive():
249
+ while True:
250
+ await asyncio.sleep(5)
251
+ await vespa_app.keepalive()
252
+ logger.debug(f"Vespa keepalive: {time.time()}")
253
+
254
+
255
+ @threaded
256
+ def get_and_store_sim_maps(
257
+ query_id, query: str, q_embs, ranking, idx_to_token, doc_ids
258
+ ):
259
+ ranking_sim = ranking + "_sim"
260
+ vespa_sim_maps = vespa_app.get_sim_maps_from_query(
261
+ query=query,
262
+ q_embs=q_embs,
263
+ ranking=ranking_sim,
264
+ idx_to_token=idx_to_token,
265
+ )
266
+ img_paths = [IMG_DIR / f"{doc_id}.jpg" for doc_id in doc_ids]
267
+ # All images should be downloaded, but best to wait 5 secs
268
+ max_wait = 5
269
+ start_time = time.time()
270
+ while (
271
+ not all([os.path.exists(img_path) for img_path in img_paths])
272
+ and time.time() - start_time < max_wait
273
+ ):
274
+ time.sleep(0.2)
275
+ if not all([os.path.exists(img_path) for img_path in img_paths]):
276
+ logger.warning(f"Images not ready in 5 seconds for query_id: {query_id}")
277
+ return False
278
+ sim_map_generator = app.sim_map_generator.gen_similarity_maps(
279
+ query=query,
280
+ query_embs=q_embs,
281
+ token_idx_map=idx_to_token,
282
+ images=img_paths,
283
+ vespa_sim_maps=vespa_sim_maps,
284
+ )
285
+ for idx, token, token_idx, blended_img_base64 in sim_map_generator:
286
+ with open(SIM_MAP_DIR / f"{query_id}_{idx}_{token_idx}.png", "wb") as f:
287
+ f.write(base64.b64decode(blended_img_base64))
288
+ logger.debug(
289
+ f"Sim map saved to disk for query_id: {query_id}, idx: {idx}, token: {token}"
290
+ )
291
+ return True
292
+
293
+
294
+ @app.get("/get_sim_map")
295
+ async def get_sim_map(query_id: str, idx: int, token: str, token_idx: int):
296
+ """
297
+ Endpoint that each of the sim map button polls to get the sim map image
298
+ when it is ready. If it is not ready, returns a SimMapButtonPoll, that
299
+ continues to poll every 1 second.
300
+ """
301
+ sim_map_path = SIM_MAP_DIR / f"{query_id}_{idx}_{token_idx}.png"
302
+ if not os.path.exists(sim_map_path):
303
+ logger.debug(
304
+ f"Sim map not ready for query_id: {query_id}, idx: {idx}, token: {token}"
305
+ )
306
+ return SimMapButtonPoll(
307
+ query_id=query_id, idx=idx, token=token, token_idx=token_idx
308
+ )
309
+ else:
310
+ return SimMapButtonReady(
311
+ query_id=query_id,
312
+ idx=idx,
313
+ token=token,
314
+ token_idx=token_idx,
315
+ img_src=sim_map_path,
316
+ )
317
+
318
+
319
+ @app.get("/full_image")
320
+ async def full_image(doc_id: str):
321
+ """
322
+ Endpoint to get the full quality image for a given result id.
323
+ """
324
+ img_path = IMG_DIR / f"{doc_id}.jpg"
325
+ if not os.path.exists(img_path):
326
+ image_data = await vespa_app.get_full_image_from_vespa(doc_id)
327
+ # image data is base 64 encoded string. Save it to disk as jpg.
328
+ with open(img_path, "wb") as f:
329
+ f.write(base64.b64decode(image_data))
330
+ logger.debug(f"Full image saved to disk for doc_id: {doc_id}")
331
+ else:
332
+ with open(img_path, "rb") as f:
333
+ image_data = base64.b64encode(f.read()).decode("utf-8")
334
+ return Img(
335
+ src=f"data:image/jpeg;base64,{image_data}",
336
+ alt="something",
337
+ cls="result-image w-full h-full object-contain",
338
+ )
339
+
340
+
341
+ @rt("/suggestions")
342
+ async def get_suggestions(query: str = ""):
343
+ """Endpoint to get suggestions as user types in the search box"""
344
+ query = query.lower().strip()
345
+
346
+ if query:
347
+ suggestions = await vespa_app.get_suggestions(query)
348
+ if len(suggestions) > 0:
349
+ return JSONResponse({"suggestions": suggestions})
350
+
351
+ return JSONResponse({"suggestions": []})
352
+
353
+
354
+ async def message_generator(query_id: str, query: str, doc_ids: list):
355
+ """Generator function to yield SSE messages for chat response"""
356
+ images = []
357
+ num_images = 3 # Number of images before firing chat request
358
+ max_wait = 10 # seconds
359
+ start_time = time.time()
360
+ # Check if full images are ready on disk
361
+ while (
362
+ len(images) < min(num_images, len(doc_ids))
363
+ and time.time() - start_time < max_wait
364
+ ):
365
+ images = []
366
+ for idx in range(num_images):
367
+ image_filename = IMG_DIR / f"{doc_ids[idx]}.jpg"
368
+ if not os.path.exists(image_filename):
369
+ logger.debug(
370
+ f"Message generator: Full image not ready for query_id: {query_id}, idx: {idx}"
371
+ )
372
+ continue
373
+ else:
374
+ logger.debug(
375
+ f"Message generator: image ready for query_id: {query_id}, idx: {idx}"
376
+ )
377
+ images.append(Image.open(image_filename))
378
+ if len(images) < num_images:
379
+ await asyncio.sleep(0.2)
380
+
381
+ # yield message with number of images ready
382
+ yield f"event: message\ndata: Generating response based on {len(images)} images...\n\n"
383
+ if not images:
384
+ yield "event: message\ndata: Failed to send images to Gemini-8B!\n\n"
385
+ yield "event: close\ndata: \n\n"
386
+ return
387
+
388
+ # If newlines are present in the response, the connection will be closed.
389
+ def replace_newline_with_br(text):
390
+ return text.replace("\n", "<br>")
391
+
392
+ response_text = ""
393
+ async for chunk in await gemini_model.generate_content_async(
394
+ images + ["\n\n Query: ", query], stream=True
395
+ ):
396
+ if chunk.text:
397
+ response_text += chunk.text
398
+ response_text = replace_newline_with_br(response_text)
399
+ yield f"event: message\ndata: {response_text}\n\n"
400
+ await asyncio.sleep(0.1)
401
+ yield "event: close\ndata: \n\n"
402
+
403
+
404
+ @app.get("/get-message")
405
+ async def get_message(query_id: str, query: str, doc_ids: str):
406
+ return StreamingResponse(
407
+ message_generator(query_id=query_id, query=query, doc_ids=doc_ids.split(",")),
408
+ media_type="text/event-stream",
409
+ )
410
+
411
+
412
+ @rt("/app")
413
+ def get():
414
+ return Layout(Main(Div(P(f"Connected to Vespa at {vespa_app.url}"), cls="p-4")))
415
+
416
+
417
+ if __name__ == "__main__":
418
+ HOT_RELOAD = os.getenv("HOT_RELOAD", "False").lower() == "true"
419
+ logger.info(f"Starting app with hot reload: {HOT_RELOAD}")
420
+ serve(port=7860, reload=HOT_RELOAD)
output.css ADDED
@@ -0,0 +1,2973 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *, ::before, ::after {
2
+ --tw-border-spacing-x: 0;
3
+ --tw-border-spacing-y: 0;
4
+ --tw-translate-x: 0;
5
+ --tw-translate-y: 0;
6
+ --tw-rotate: 0;
7
+ --tw-skew-x: 0;
8
+ --tw-skew-y: 0;
9
+ --tw-scale-x: 1;
10
+ --tw-scale-y: 1;
11
+ --tw-pan-x: ;
12
+ --tw-pan-y: ;
13
+ --tw-pinch-zoom: ;
14
+ --tw-scroll-snap-strictness: proximity;
15
+ --tw-gradient-from-position: ;
16
+ --tw-gradient-via-position: ;
17
+ --tw-gradient-to-position: ;
18
+ --tw-ordinal: ;
19
+ --tw-slashed-zero: ;
20
+ --tw-numeric-figure: ;
21
+ --tw-numeric-spacing: ;
22
+ --tw-numeric-fraction: ;
23
+ --tw-ring-inset: ;
24
+ --tw-ring-offset-width: 0px;
25
+ --tw-ring-offset-color: #fff;
26
+ --tw-ring-color: rgb(59 130 246 / 0.5);
27
+ --tw-ring-offset-shadow: 0 0 #0000;
28
+ --tw-ring-shadow: 0 0 #0000;
29
+ --tw-shadow: 0 0 #0000;
30
+ --tw-shadow-colored: 0 0 #0000;
31
+ --tw-blur: ;
32
+ --tw-brightness: ;
33
+ --tw-contrast: ;
34
+ --tw-grayscale: ;
35
+ --tw-hue-rotate: ;
36
+ --tw-invert: ;
37
+ --tw-saturate: ;
38
+ --tw-sepia: ;
39
+ --tw-drop-shadow: ;
40
+ --tw-backdrop-blur: ;
41
+ --tw-backdrop-brightness: ;
42
+ --tw-backdrop-contrast: ;
43
+ --tw-backdrop-grayscale: ;
44
+ --tw-backdrop-hue-rotate: ;
45
+ --tw-backdrop-invert: ;
46
+ --tw-backdrop-opacity: ;
47
+ --tw-backdrop-saturate: ;
48
+ --tw-backdrop-sepia: ;
49
+ --tw-contain-size: ;
50
+ --tw-contain-layout: ;
51
+ --tw-contain-paint: ;
52
+ --tw-contain-style: ;
53
+ }
54
+
55
+ ::backdrop {
56
+ --tw-border-spacing-x: 0;
57
+ --tw-border-spacing-y: 0;
58
+ --tw-translate-x: 0;
59
+ --tw-translate-y: 0;
60
+ --tw-rotate: 0;
61
+ --tw-skew-x: 0;
62
+ --tw-skew-y: 0;
63
+ --tw-scale-x: 1;
64
+ --tw-scale-y: 1;
65
+ --tw-pan-x: ;
66
+ --tw-pan-y: ;
67
+ --tw-pinch-zoom: ;
68
+ --tw-scroll-snap-strictness: proximity;
69
+ --tw-gradient-from-position: ;
70
+ --tw-gradient-via-position: ;
71
+ --tw-gradient-to-position: ;
72
+ --tw-ordinal: ;
73
+ --tw-slashed-zero: ;
74
+ --tw-numeric-figure: ;
75
+ --tw-numeric-spacing: ;
76
+ --tw-numeric-fraction: ;
77
+ --tw-ring-inset: ;
78
+ --tw-ring-offset-width: 0px;
79
+ --tw-ring-offset-color: #fff;
80
+ --tw-ring-color: rgb(59 130 246 / 0.5);
81
+ --tw-ring-offset-shadow: 0 0 #0000;
82
+ --tw-ring-shadow: 0 0 #0000;
83
+ --tw-shadow: 0 0 #0000;
84
+ --tw-shadow-colored: 0 0 #0000;
85
+ --tw-blur: ;
86
+ --tw-brightness: ;
87
+ --tw-contrast: ;
88
+ --tw-grayscale: ;
89
+ --tw-hue-rotate: ;
90
+ --tw-invert: ;
91
+ --tw-saturate: ;
92
+ --tw-sepia: ;
93
+ --tw-drop-shadow: ;
94
+ --tw-backdrop-blur: ;
95
+ --tw-backdrop-brightness: ;
96
+ --tw-backdrop-contrast: ;
97
+ --tw-backdrop-grayscale: ;
98
+ --tw-backdrop-hue-rotate: ;
99
+ --tw-backdrop-invert: ;
100
+ --tw-backdrop-opacity: ;
101
+ --tw-backdrop-saturate: ;
102
+ --tw-backdrop-sepia: ;
103
+ --tw-contain-size: ;
104
+ --tw-contain-layout: ;
105
+ --tw-contain-paint: ;
106
+ --tw-contain-style: ;
107
+ }
108
+
109
+ /*
110
+ ! tailwindcss v3.4.13 | MIT License | https://tailwindcss.com
111
+ */
112
+
113
+ /*
114
+ 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
115
+ 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
116
+ */
117
+
118
+ *,
119
+ ::before,
120
+ ::after {
121
+ box-sizing: border-box;
122
+ /* 1 */
123
+ border-width: 0;
124
+ /* 2 */
125
+ border-style: solid;
126
+ /* 2 */
127
+ border-color: #e5e7eb;
128
+ /* 2 */
129
+ }
130
+
131
+ ::before,
132
+ ::after {
133
+ --tw-content: '';
134
+ }
135
+
136
+ /*
137
+ 1. Use a consistent sensible line-height in all browsers.
138
+ 2. Prevent adjustments of font size after orientation changes in iOS.
139
+ 3. Use a more readable tab size.
140
+ 4. Use the user's configured `sans` font-family by default.
141
+ 5. Use the user's configured `sans` font-feature-settings by default.
142
+ 6. Use the user's configured `sans` font-variation-settings by default.
143
+ 7. Disable tap highlights on iOS
144
+ */
145
+
146
+ html,
147
+ :host {
148
+ line-height: 1.5;
149
+ /* 1 */
150
+ -webkit-text-size-adjust: 100%;
151
+ /* 2 */
152
+ -moz-tab-size: 4;
153
+ /* 3 */
154
+ -o-tab-size: 4;
155
+ tab-size: 4;
156
+ /* 3 */
157
+ font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
158
+ /* 4 */
159
+ font-feature-settings: normal;
160
+ /* 5 */
161
+ font-variation-settings: normal;
162
+ /* 6 */
163
+ -webkit-tap-highlight-color: transparent;
164
+ /* 7 */
165
+ }
166
+
167
+ /*
168
+ 1. Remove the margin in all browsers.
169
+ 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
170
+ */
171
+
172
+ body {
173
+ margin: 0;
174
+ /* 1 */
175
+ line-height: inherit;
176
+ /* 2 */
177
+ }
178
+
179
+ /*
180
+ 1. Add the correct height in Firefox.
181
+ 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
182
+ 3. Ensure horizontal rules are visible by default.
183
+ */
184
+
185
+ hr {
186
+ height: 0;
187
+ /* 1 */
188
+ color: inherit;
189
+ /* 2 */
190
+ border-top-width: 1px;
191
+ /* 3 */
192
+ }
193
+
194
+ /*
195
+ Add the correct text decoration in Chrome, Edge, and Safari.
196
+ */
197
+
198
+ abbr:where([title]) {
199
+ -webkit-text-decoration: underline dotted;
200
+ text-decoration: underline dotted;
201
+ }
202
+
203
+ /*
204
+ Remove the default font size and weight for headings.
205
+ */
206
+
207
+ h1,
208
+ h2,
209
+ h3,
210
+ h4,
211
+ h5,
212
+ h6 {
213
+ font-size: inherit;
214
+ font-weight: inherit;
215
+ }
216
+
217
+ /*
218
+ Reset links to optimize for opt-in styling instead of opt-out.
219
+ */
220
+
221
+ a {
222
+ color: inherit;
223
+ text-decoration: inherit;
224
+ }
225
+
226
+ /*
227
+ Add the correct font weight in Edge and Safari.
228
+ */
229
+
230
+ b,
231
+ strong {
232
+ font-weight: bolder;
233
+ }
234
+
235
+ /*
236
+ 1. Use the user's configured `mono` font-family by default.
237
+ 2. Use the user's configured `mono` font-feature-settings by default.
238
+ 3. Use the user's configured `mono` font-variation-settings by default.
239
+ 4. Correct the odd `em` font sizing in all browsers.
240
+ */
241
+
242
+ code,
243
+ kbd,
244
+ samp,
245
+ pre {
246
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
247
+ /* 1 */
248
+ font-feature-settings: normal;
249
+ /* 2 */
250
+ font-variation-settings: normal;
251
+ /* 3 */
252
+ font-size: 1em;
253
+ /* 4 */
254
+ }
255
+
256
+ /*
257
+ Add the correct font size in all browsers.
258
+ */
259
+
260
+ small {
261
+ font-size: 80%;
262
+ }
263
+
264
+ /*
265
+ Prevent `sub` and `sup` elements from affecting the line height in all browsers.
266
+ */
267
+
268
+ sub,
269
+ sup {
270
+ font-size: 75%;
271
+ line-height: 0;
272
+ position: relative;
273
+ vertical-align: baseline;
274
+ }
275
+
276
+ sub {
277
+ bottom: -0.25em;
278
+ }
279
+
280
+ sup {
281
+ top: -0.5em;
282
+ }
283
+
284
+ /*
285
+ 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
286
+ 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
287
+ 3. Remove gaps between table borders by default.
288
+ */
289
+
290
+ table {
291
+ text-indent: 0;
292
+ /* 1 */
293
+ border-color: inherit;
294
+ /* 2 */
295
+ border-collapse: collapse;
296
+ /* 3 */
297
+ }
298
+
299
+ /*
300
+ 1. Change the font styles in all browsers.
301
+ 2. Remove the margin in Firefox and Safari.
302
+ 3. Remove default padding in all browsers.
303
+ */
304
+
305
+ button,
306
+ input,
307
+ optgroup,
308
+ select,
309
+ textarea {
310
+ font-family: inherit;
311
+ /* 1 */
312
+ font-feature-settings: inherit;
313
+ /* 1 */
314
+ font-variation-settings: inherit;
315
+ /* 1 */
316
+ font-size: 100%;
317
+ /* 1 */
318
+ font-weight: inherit;
319
+ /* 1 */
320
+ line-height: inherit;
321
+ /* 1 */
322
+ letter-spacing: inherit;
323
+ /* 1 */
324
+ color: inherit;
325
+ /* 1 */
326
+ margin: 0;
327
+ /* 2 */
328
+ padding: 0;
329
+ /* 3 */
330
+ }
331
+
332
+ /*
333
+ Remove the inheritance of text transform in Edge and Firefox.
334
+ */
335
+
336
+ button,
337
+ select {
338
+ text-transform: none;
339
+ }
340
+
341
+ /*
342
+ 1. Correct the inability to style clickable types in iOS and Safari.
343
+ 2. Remove default button styles.
344
+ */
345
+
346
+ button,
347
+ input:where([type='button']),
348
+ input:where([type='reset']),
349
+ input:where([type='submit']) {
350
+ -webkit-appearance: button;
351
+ /* 1 */
352
+ background-color: transparent;
353
+ /* 2 */
354
+ background-image: none;
355
+ /* 2 */
356
+ }
357
+
358
+ /*
359
+ Use the modern Firefox focus style for all focusable elements.
360
+ */
361
+
362
+ :-moz-focusring {
363
+ outline: auto;
364
+ }
365
+
366
+ /*
367
+ Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
368
+ */
369
+
370
+ :-moz-ui-invalid {
371
+ box-shadow: none;
372
+ }
373
+
374
+ /*
375
+ Add the correct vertical alignment in Chrome and Firefox.
376
+ */
377
+
378
+ progress {
379
+ vertical-align: baseline;
380
+ }
381
+
382
+ /*
383
+ Correct the cursor style of increment and decrement buttons in Safari.
384
+ */
385
+
386
+ ::-webkit-inner-spin-button,
387
+ ::-webkit-outer-spin-button {
388
+ height: auto;
389
+ }
390
+
391
+ /*
392
+ 1. Correct the odd appearance in Chrome and Safari.
393
+ 2. Correct the outline style in Safari.
394
+ */
395
+
396
+ [type='search'] {
397
+ -webkit-appearance: textfield;
398
+ /* 1 */
399
+ outline-offset: -2px;
400
+ /* 2 */
401
+ }
402
+
403
+ /*
404
+ Remove the inner padding in Chrome and Safari on macOS.
405
+ */
406
+
407
+ ::-webkit-search-decoration {
408
+ -webkit-appearance: none;
409
+ }
410
+
411
+ /*
412
+ 1. Correct the inability to style clickable types in iOS and Safari.
413
+ 2. Change font properties to `inherit` in Safari.
414
+ */
415
+
416
+ ::-webkit-file-upload-button {
417
+ -webkit-appearance: button;
418
+ /* 1 */
419
+ font: inherit;
420
+ /* 2 */
421
+ }
422
+
423
+ /*
424
+ Add the correct display in Chrome and Safari.
425
+ */
426
+
427
+ summary {
428
+ display: list-item;
429
+ }
430
+
431
+ /*
432
+ Removes the default spacing and border for appropriate elements.
433
+ */
434
+
435
+ blockquote,
436
+ dl,
437
+ dd,
438
+ h1,
439
+ h2,
440
+ h3,
441
+ h4,
442
+ h5,
443
+ h6,
444
+ hr,
445
+ figure,
446
+ p,
447
+ pre {
448
+ margin: 0;
449
+ }
450
+
451
+ fieldset {
452
+ margin: 0;
453
+ padding: 0;
454
+ }
455
+
456
+ legend {
457
+ padding: 0;
458
+ }
459
+
460
+ ol,
461
+ ul,
462
+ menu {
463
+ list-style: none;
464
+ margin: 0;
465
+ padding: 0;
466
+ }
467
+
468
+ /*
469
+ Reset default styling for dialogs.
470
+ */
471
+
472
+ dialog {
473
+ padding: 0;
474
+ }
475
+
476
+ /*
477
+ Prevent resizing textareas horizontally by default.
478
+ */
479
+
480
+ textarea {
481
+ resize: vertical;
482
+ }
483
+
484
+ /*
485
+ 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
486
+ 2. Set the default placeholder color to the user's configured gray 400 color.
487
+ */
488
+
489
+ input::-moz-placeholder, textarea::-moz-placeholder {
490
+ opacity: 1;
491
+ /* 1 */
492
+ color: #9ca3af;
493
+ /* 2 */
494
+ }
495
+
496
+ input::placeholder,
497
+ textarea::placeholder {
498
+ opacity: 1;
499
+ /* 1 */
500
+ color: #9ca3af;
501
+ /* 2 */
502
+ }
503
+
504
+ /*
505
+ Set the default cursor for buttons.
506
+ */
507
+
508
+ button,
509
+ [role="button"] {
510
+ cursor: pointer;
511
+ }
512
+
513
+ /*
514
+ Make sure disabled buttons don't get the pointer cursor.
515
+ */
516
+
517
+ :disabled {
518
+ cursor: default;
519
+ }
520
+
521
+ /*
522
+ 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
523
+ 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
524
+ This can trigger a poorly considered lint error in some tools but is included by design.
525
+ */
526
+
527
+ img,
528
+ svg,
529
+ video,
530
+ canvas,
531
+ audio,
532
+ iframe,
533
+ embed,
534
+ object {
535
+ display: block;
536
+ /* 1 */
537
+ vertical-align: middle;
538
+ /* 2 */
539
+ }
540
+
541
+ /*
542
+ Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
543
+ */
544
+
545
+ img,
546
+ video {
547
+ max-width: 100%;
548
+ height: auto;
549
+ }
550
+
551
+ /* Make elements with the HTML hidden attribute stay hidden by default */
552
+
553
+ [hidden] {
554
+ display: none;
555
+ }
556
+
557
+ :root {
558
+ --background: 240 20% 99%;
559
+ /* 1 */
560
+ --foreground: 210 13% 13%;
561
+ /* 12 */
562
+ --card: 240 20% 99%;
563
+ /* 1 */
564
+ --card-foreground: 210 13% 13%;
565
+ /* 12 */
566
+ --popover: 240 20% 99%;
567
+ /* 1 */
568
+ --popover-foreground: 210 13% 13%;
569
+ /* 12 */
570
+ --primary: 210 13% 13%;
571
+ /* 12 */
572
+ --primary-foreground: 240 20% 98%;
573
+ /* 2 */
574
+ --secondary: 240 11% 95%;
575
+ /* 3 */
576
+ --secondary-foreground: 210 13% 13%;
577
+ /* 12 */
578
+ --muted: 240 11% 95%;
579
+ /* 3 */
580
+ --muted-foreground: 220 6% 40%;
581
+ /* 11 */
582
+ --accent: 240 11% 95%;
583
+ /* 3 */
584
+ --accent-foreground: 210 13% 13%;
585
+ /* 12 */
586
+ --destructive: 358 75% 59%;
587
+ /* 9 - red */
588
+ --destructive-foreground: 240 20% 98%;
589
+ /* 2 */
590
+ --border: 240 10% 86%;
591
+ /* 6 */
592
+ --input: 240 10% 86%;
593
+ /* 6 */
594
+ --ring: 210 13% 13%;
595
+ /* 12 */
596
+ --chart-1: 10 78% 54%;
597
+ /* 9 - tomato */
598
+ --chart-2: 173 80% 36%;
599
+ /* 9 - teal */
600
+ --chart-3: 206 100% 50%;
601
+ /* 9 - blue */
602
+ --chart-4: 42 100% 62%;
603
+ /* 9 - amber */
604
+ --chart-5: 23 93% 53%;
605
+ /* 9 - orange */
606
+ }
607
+
608
+ .dark {
609
+ --background: 240 6% 7%;
610
+ /* 1 */
611
+ --foreground: 220 9% 94%;
612
+ /* 12 */
613
+ --card: 240 6% 7%;
614
+ /* 1 */
615
+ --card-foreground: 220 9% 94%;
616
+ /* 12 */
617
+ --popover: 240 6% 7%;
618
+ /* 1 */
619
+ --popover-foreground: 220 9% 94%;
620
+ /* 12 */
621
+ --primary: 220 9% 94%;
622
+ /* 12 */
623
+ --primary-foreground: 220 6% 10%;
624
+ /* 2 */
625
+ --secondary: 225 6% 14%;
626
+ /* 3 */
627
+ --secondary-foreground: 220 9% 94%;
628
+ /* 12 */
629
+ --muted: 225 6% 14%;
630
+ /* 3 */
631
+ --muted-foreground: 216 7% 71%;
632
+ /* 11 */
633
+ --accent: 225 6% 14%;
634
+ /* 3 */
635
+ --accent-foreground: 220 9% 94%;
636
+ /* 12 */
637
+ --destructive: 358 75% 59%;
638
+ /* 9 - red */
639
+ --destructive-foreground: 220 9% 94%;
640
+ /* 12 */
641
+ --border: 213 8% 23%;
642
+ /* 6 */
643
+ --input: 213 8% 23%;
644
+ /* 6 */
645
+ --ring: 220 9% 94%;
646
+ /* 12 */
647
+ --chart-1: 10 78% 54%;
648
+ /* 9 - tomato */
649
+ --chart-2: 173 80% 36%;
650
+ /* 9 - teal */
651
+ --chart-3: 206 100% 50%;
652
+ /* 9 - blue */
653
+ --chart-4: 42 100% 62%;
654
+ /* 9 - amber */
655
+ --chart-5: 23 93% 53%;
656
+ /* 9 - orange */
657
+ }
658
+
659
+ :root:has(.no-bg-scroll) {
660
+ overflow: hidden;
661
+ }
662
+
663
+ * {
664
+ border-color: hsl(var(--border));
665
+ }
666
+
667
+ body {
668
+ min-height: 100vh;
669
+ background-color: hsl(var(--background));
670
+ color: hsl(var(--foreground));
671
+ -webkit-font-smoothing: antialiased;
672
+ -moz-osx-font-smoothing: grayscale;
673
+ font-feature-settings: "rlig" 1, "calt" 1;
674
+ }
675
+
676
+ .container {
677
+ width: 100%;
678
+ margin-right: auto;
679
+ margin-left: auto;
680
+ padding-right: 2rem;
681
+ padding-left: 2rem;
682
+ }
683
+
684
+ @media (min-width: 1400px) {
685
+ .container {
686
+ max-width: 1400px;
687
+ }
688
+ }
689
+
690
+ .sr-only {
691
+ position: absolute;
692
+ width: 1px;
693
+ height: 1px;
694
+ padding: 0;
695
+ margin: -1px;
696
+ overflow: hidden;
697
+ clip: rect(0, 0, 0, 0);
698
+ white-space: nowrap;
699
+ border-width: 0;
700
+ }
701
+
702
+ .pointer-events-none {
703
+ pointer-events: none;
704
+ }
705
+
706
+ .pointer-events-auto {
707
+ pointer-events: auto;
708
+ }
709
+
710
+ .invisible {
711
+ visibility: hidden;
712
+ }
713
+
714
+ .static {
715
+ position: static;
716
+ }
717
+
718
+ .fixed {
719
+ position: fixed;
720
+ }
721
+
722
+ .absolute {
723
+ position: absolute;
724
+ }
725
+
726
+ .relative {
727
+ position: relative;
728
+ }
729
+
730
+ .inset-0 {
731
+ inset: 0px;
732
+ }
733
+
734
+ .inset-x-0 {
735
+ left: 0px;
736
+ right: 0px;
737
+ }
738
+
739
+ .inset-y-0 {
740
+ top: 0px;
741
+ bottom: 0px;
742
+ }
743
+
744
+ .-bottom-12 {
745
+ bottom: -3rem;
746
+ }
747
+
748
+ .-left-12 {
749
+ left: -3rem;
750
+ }
751
+
752
+ .-right-12 {
753
+ right: -3rem;
754
+ }
755
+
756
+ .-top-12 {
757
+ top: -3rem;
758
+ }
759
+
760
+ .bottom-0 {
761
+ bottom: 0px;
762
+ }
763
+
764
+ .left-0 {
765
+ left: 0px;
766
+ }
767
+
768
+ .left-1\/2 {
769
+ left: 50%;
770
+ }
771
+
772
+ .left-2 {
773
+ left: 0.5rem;
774
+ }
775
+
776
+ .left-\[50\%\] {
777
+ left: 50%;
778
+ }
779
+
780
+ .right-0 {
781
+ right: 0px;
782
+ }
783
+
784
+ .right-2 {
785
+ right: 0.5rem;
786
+ }
787
+
788
+ .right-4 {
789
+ right: 1rem;
790
+ }
791
+
792
+ .top-0 {
793
+ top: 0px;
794
+ }
795
+
796
+ .top-1\/2 {
797
+ top: 50%;
798
+ }
799
+
800
+ .top-2 {
801
+ top: 0.5rem;
802
+ }
803
+
804
+ .top-4 {
805
+ top: 1rem;
806
+ }
807
+
808
+ .top-\[50\%\] {
809
+ top: 50%;
810
+ }
811
+
812
+ .top-full {
813
+ top: 100%;
814
+ }
815
+
816
+ .z-10 {
817
+ z-index: 10;
818
+ }
819
+
820
+ .z-40 {
821
+ z-index: 40;
822
+ }
823
+
824
+ .z-50 {
825
+ z-index: 50;
826
+ }
827
+
828
+ .z-\[100\] {
829
+ z-index: 100;
830
+ }
831
+
832
+ .-mx-1 {
833
+ margin-left: -0.25rem;
834
+ margin-right: -0.25rem;
835
+ }
836
+
837
+ .mx-auto {
838
+ margin-left: auto;
839
+ margin-right: auto;
840
+ }
841
+
842
+ .my-1 {
843
+ margin-top: 0.25rem;
844
+ margin-bottom: 0.25rem;
845
+ }
846
+
847
+ .-ml-4 {
848
+ margin-left: -1rem;
849
+ }
850
+
851
+ .-mt-4 {
852
+ margin-top: -1rem;
853
+ }
854
+
855
+ .mb-1 {
856
+ margin-bottom: 0.25rem;
857
+ }
858
+
859
+ .mr-1\.5 {
860
+ margin-right: 0.375rem;
861
+ }
862
+
863
+ .mt-2 {
864
+ margin-top: 0.5rem;
865
+ }
866
+
867
+ .mt-\[13vh\] {
868
+ margin-top: 13vh;
869
+ }
870
+
871
+ .mt-\[8vh\] {
872
+ margin-top: 8vh;
873
+ }
874
+
875
+ .mt-8 {
876
+ margin-top: 2rem;
877
+ }
878
+
879
+ .mt-5 {
880
+ margin-top: 1.25rem;
881
+ }
882
+
883
+ .block {
884
+ display: block;
885
+ }
886
+
887
+ .flex {
888
+ display: flex;
889
+ }
890
+
891
+ .inline-flex {
892
+ display: inline-flex;
893
+ }
894
+
895
+ .table {
896
+ display: table;
897
+ }
898
+
899
+ .grid {
900
+ display: grid;
901
+ }
902
+
903
+ .contents {
904
+ display: contents;
905
+ }
906
+
907
+ .hidden {
908
+ display: none;
909
+ }
910
+
911
+ .aspect-square {
912
+ aspect-ratio: 1 / 1;
913
+ }
914
+
915
+ .size-4 {
916
+ width: 1rem;
917
+ height: 1rem;
918
+ }
919
+
920
+ .size-5 {
921
+ width: 1.25rem;
922
+ height: 1.25rem;
923
+ }
924
+
925
+ .h-10 {
926
+ height: 2.5rem;
927
+ }
928
+
929
+ .h-11 {
930
+ height: 2.75rem;
931
+ }
932
+
933
+ .h-12 {
934
+ height: 3rem;
935
+ }
936
+
937
+ .h-2 {
938
+ height: 0.5rem;
939
+ }
940
+
941
+ .h-2\.5 {
942
+ height: 0.625rem;
943
+ }
944
+
945
+ .h-3\.5 {
946
+ height: 0.875rem;
947
+ }
948
+
949
+ .h-4 {
950
+ height: 1rem;
951
+ }
952
+
953
+ .h-5 {
954
+ height: 1.25rem;
955
+ }
956
+
957
+ .h-6 {
958
+ height: 1.5rem;
959
+ }
960
+
961
+ .h-8 {
962
+ height: 2rem;
963
+ }
964
+
965
+ .h-9 {
966
+ height: 2.25rem;
967
+ }
968
+
969
+ .h-\[1\.5px\] {
970
+ height: 1.5px;
971
+ }
972
+
973
+ .h-\[27px\] {
974
+ height: 27px;
975
+ }
976
+
977
+ .h-\[377px\] {
978
+ height: 377px;
979
+ }
980
+
981
+ .h-\[55px\] {
982
+ height: 55px;
983
+ }
984
+
985
+ .h-full {
986
+ height: 100%;
987
+ }
988
+
989
+ .h-px {
990
+ height: 1px;
991
+ }
992
+
993
+ .h-\[21px\] {
994
+ height: 21px;
995
+ }
996
+
997
+ .max-h-96 {
998
+ max-height: 24rem;
999
+ }
1000
+
1001
+ .max-h-screen {
1002
+ max-height: 100vh;
1003
+ }
1004
+
1005
+ .min-h-0 {
1006
+ min-height: 0px;
1007
+ }
1008
+
1009
+ .min-h-\[55px\] {
1010
+ min-height: 55px;
1011
+ }
1012
+
1013
+ .min-h-\[80px\] {
1014
+ min-height: 80px;
1015
+ }
1016
+
1017
+ .min-h-screen {
1018
+ min-height: 100vh;
1019
+ }
1020
+
1021
+ .w-10 {
1022
+ width: 2.5rem;
1023
+ }
1024
+
1025
+ .w-11 {
1026
+ width: 2.75rem;
1027
+ }
1028
+
1029
+ .w-2\.5 {
1030
+ width: 0.625rem;
1031
+ }
1032
+
1033
+ .w-3\.5 {
1034
+ width: 0.875rem;
1035
+ }
1036
+
1037
+ .w-3\/4 {
1038
+ width: 75%;
1039
+ }
1040
+
1041
+ .w-4 {
1042
+ width: 1rem;
1043
+ }
1044
+
1045
+ .w-48 {
1046
+ width: 12rem;
1047
+ }
1048
+
1049
+ .w-5 {
1050
+ width: 1.25rem;
1051
+ }
1052
+
1053
+ .w-8 {
1054
+ width: 2rem;
1055
+ }
1056
+
1057
+ .w-9 {
1058
+ width: 2.25rem;
1059
+ }
1060
+
1061
+ .w-\[1\.5px\] {
1062
+ width: 1.5px;
1063
+ }
1064
+
1065
+ .w-full {
1066
+ width: 100%;
1067
+ }
1068
+
1069
+ .min-w-0 {
1070
+ min-width: 0px;
1071
+ }
1072
+
1073
+ .min-w-\[8rem\] {
1074
+ min-width: 8rem;
1075
+ }
1076
+
1077
+ .max-w-lg {
1078
+ max-width: 32rem;
1079
+ }
1080
+
1081
+ .max-w-screen-md {
1082
+ max-width: 768px;
1083
+ }
1084
+
1085
+ .flex-1 {
1086
+ flex: 1 1 0%;
1087
+ }
1088
+
1089
+ .shrink-0 {
1090
+ flex-shrink: 0;
1091
+ }
1092
+
1093
+ .grow {
1094
+ flex-grow: 1;
1095
+ }
1096
+
1097
+ .grow-0 {
1098
+ flex-grow: 0;
1099
+ }
1100
+
1101
+ .basis-full {
1102
+ flex-basis: 100%;
1103
+ }
1104
+
1105
+ .caption-bottom {
1106
+ caption-side: bottom;
1107
+ }
1108
+
1109
+ .-translate-x-1\/2 {
1110
+ --tw-translate-x: -50%;
1111
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1112
+ }
1113
+
1114
+ .-translate-y-1\/2 {
1115
+ --tw-translate-y: -50%;
1116
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1117
+ }
1118
+
1119
+ .translate-x-\[-50\%\] {
1120
+ --tw-translate-x: -50%;
1121
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1122
+ }
1123
+
1124
+ .translate-y-\[-50\%\] {
1125
+ --tw-translate-y: -50%;
1126
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1127
+ }
1128
+
1129
+ .rotate-90 {
1130
+ --tw-rotate: 90deg;
1131
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1132
+ }
1133
+
1134
+ .transform {
1135
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1136
+ }
1137
+
1138
+ @keyframes pulse {
1139
+ 50% {
1140
+ opacity: .5;
1141
+ }
1142
+ }
1143
+
1144
+ .animate-pulse {
1145
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
1146
+ }
1147
+
1148
+ @keyframes spin {
1149
+ to {
1150
+ transform: rotate(360deg);
1151
+ }
1152
+ }
1153
+
1154
+ .animate-spin {
1155
+ animation: spin 1s linear infinite;
1156
+ }
1157
+
1158
+ .cursor-default {
1159
+ cursor: default;
1160
+ }
1161
+
1162
+ .cursor-pointer {
1163
+ cursor: pointer;
1164
+ }
1165
+
1166
+ .touch-none {
1167
+ touch-action: none;
1168
+ }
1169
+
1170
+ .select-none {
1171
+ -webkit-user-select: none;
1172
+ -moz-user-select: none;
1173
+ user-select: none;
1174
+ }
1175
+
1176
+ .resize {
1177
+ resize: both;
1178
+ }
1179
+
1180
+ .grid-flow-col {
1181
+ grid-auto-flow: column;
1182
+ }
1183
+
1184
+ .grid-cols-1 {
1185
+ grid-template-columns: repeat(1, minmax(0, 1fr));
1186
+ }
1187
+
1188
+ .grid-rows-\[1fr_1fr\] {
1189
+ grid-template-rows: 1fr 1fr;
1190
+ }
1191
+
1192
+ .grid-rows-\[auto_0px\] {
1193
+ grid-template-rows: auto 0px;
1194
+ }
1195
+
1196
+ .grid-rows-\[auto_1fr_auto\] {
1197
+ grid-template-rows: auto 1fr auto;
1198
+ }
1199
+
1200
+ .grid-rows-\[auto_auto_1fr\] {
1201
+ grid-template-rows: auto auto 1fr;
1202
+ }
1203
+
1204
+ .grid-rows-\[minmax\(0\2c 55px\)_minmax\(0\2c 1fr\)\] {
1205
+ grid-template-rows: minmax(0,55px) minmax(0,1fr);
1206
+ }
1207
+
1208
+ .flex-col {
1209
+ flex-direction: column;
1210
+ }
1211
+
1212
+ .flex-col-reverse {
1213
+ flex-direction: column-reverse;
1214
+ }
1215
+
1216
+ .flex-wrap {
1217
+ flex-wrap: wrap;
1218
+ }
1219
+
1220
+ .content-center {
1221
+ align-content: center;
1222
+ }
1223
+
1224
+ .content-start {
1225
+ align-content: flex-start;
1226
+ }
1227
+
1228
+ .items-end {
1229
+ align-items: flex-end;
1230
+ }
1231
+
1232
+ .items-center {
1233
+ align-items: center;
1234
+ }
1235
+
1236
+ .justify-end {
1237
+ justify-content: flex-end;
1238
+ }
1239
+
1240
+ .justify-center {
1241
+ justify-content: center;
1242
+ }
1243
+
1244
+ .justify-between {
1245
+ justify-content: space-between;
1246
+ }
1247
+
1248
+ .justify-items-center {
1249
+ justify-items: center;
1250
+ }
1251
+
1252
+ .gap-1\.5 {
1253
+ gap: 0.375rem;
1254
+ }
1255
+
1256
+ .gap-2 {
1257
+ gap: 0.5rem;
1258
+ }
1259
+
1260
+ .gap-3 {
1261
+ gap: 0.75rem;
1262
+ }
1263
+
1264
+ .gap-4 {
1265
+ gap: 1rem;
1266
+ }
1267
+
1268
+ .gap-5 {
1269
+ gap: 1.25rem;
1270
+ }
1271
+
1272
+ .gap-8 {
1273
+ gap: 2rem;
1274
+ }
1275
+
1276
+ .gap-\[3px\] {
1277
+ gap: 3px;
1278
+ }
1279
+
1280
+ .gap-px {
1281
+ gap: 1px;
1282
+ }
1283
+
1284
+ .gap-x-1\.5 {
1285
+ -moz-column-gap: 0.375rem;
1286
+ column-gap: 0.375rem;
1287
+ }
1288
+
1289
+ .gap-x-3 {
1290
+ -moz-column-gap: 0.75rem;
1291
+ column-gap: 0.75rem;
1292
+ }
1293
+
1294
+ .gap-x-5 {
1295
+ -moz-column-gap: 1.25rem;
1296
+ column-gap: 1.25rem;
1297
+ }
1298
+
1299
+ .gap-y-3 {
1300
+ row-gap: 0.75rem;
1301
+ }
1302
+
1303
+ .gap-y-8 {
1304
+ row-gap: 2rem;
1305
+ }
1306
+
1307
+ .space-x-1 > :not([hidden]) ~ :not([hidden]) {
1308
+ --tw-space-x-reverse: 0;
1309
+ margin-right: calc(0.25rem * var(--tw-space-x-reverse));
1310
+ margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse)));
1311
+ }
1312
+
1313
+ .space-x-2 > :not([hidden]) ~ :not([hidden]) {
1314
+ --tw-space-x-reverse: 0;
1315
+ margin-right: calc(0.5rem * var(--tw-space-x-reverse));
1316
+ margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
1317
+ }
1318
+
1319
+ .space-x-4 > :not([hidden]) ~ :not([hidden]) {
1320
+ --tw-space-x-reverse: 0;
1321
+ margin-right: calc(1rem * var(--tw-space-x-reverse));
1322
+ margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
1323
+ }
1324
+
1325
+ .space-y-1\.5 > :not([hidden]) ~ :not([hidden]) {
1326
+ --tw-space-y-reverse: 0;
1327
+ margin-top: calc(0.375rem * calc(1 - var(--tw-space-y-reverse)));
1328
+ margin-bottom: calc(0.375rem * var(--tw-space-y-reverse));
1329
+ }
1330
+
1331
+ .space-y-2 > :not([hidden]) ~ :not([hidden]) {
1332
+ --tw-space-y-reverse: 0;
1333
+ margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
1334
+ margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
1335
+ }
1336
+
1337
+ .space-x-8 > :not([hidden]) ~ :not([hidden]) {
1338
+ --tw-space-x-reverse: 0;
1339
+ margin-right: calc(2rem * var(--tw-space-x-reverse));
1340
+ margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)));
1341
+ }
1342
+
1343
+ .self-stretch {
1344
+ align-self: stretch;
1345
+ }
1346
+
1347
+ .overflow-auto {
1348
+ overflow: auto;
1349
+ }
1350
+
1351
+ .overflow-hidden {
1352
+ overflow: hidden;
1353
+ }
1354
+
1355
+ .whitespace-nowrap {
1356
+ white-space: nowrap;
1357
+ }
1358
+
1359
+ .break-words {
1360
+ overflow-wrap: break-word;
1361
+ }
1362
+
1363
+ .\!rounded-full {
1364
+ border-radius: 9999px !important;
1365
+ }
1366
+
1367
+ .rounded-\[inherit\] {
1368
+ border-radius: inherit;
1369
+ }
1370
+
1371
+ .rounded-full {
1372
+ border-radius: 9999px;
1373
+ }
1374
+
1375
+ .rounded-lg {
1376
+ border-radius: var(--radius);
1377
+ }
1378
+
1379
+ .rounded-md {
1380
+ border-radius: calc(var(--radius) - 2px);
1381
+ }
1382
+
1383
+ .rounded-none {
1384
+ border-radius: 0px;
1385
+ }
1386
+
1387
+ .rounded-sm {
1388
+ border-radius: calc(var(--radius) - 4px);
1389
+ }
1390
+
1391
+ .border {
1392
+ border-width: 1px;
1393
+ }
1394
+
1395
+ .border-2 {
1396
+ border-width: 2px;
1397
+ }
1398
+
1399
+ .border-b {
1400
+ border-bottom-width: 1px;
1401
+ }
1402
+
1403
+ .border-l {
1404
+ border-left-width: 1px;
1405
+ }
1406
+
1407
+ .border-r {
1408
+ border-right-width: 1px;
1409
+ }
1410
+
1411
+ .border-t {
1412
+ border-top-width: 1px;
1413
+ }
1414
+
1415
+ .border-dashed {
1416
+ border-style: dashed;
1417
+ }
1418
+
1419
+ .border-destructive {
1420
+ border-color: hsl(var(--destructive));
1421
+ }
1422
+
1423
+ .border-destructive\/50 {
1424
+ border-color: hsl(var(--destructive) / 0.5);
1425
+ }
1426
+
1427
+ .border-input {
1428
+ border-color: hsl(var(--input));
1429
+ }
1430
+
1431
+ .border-primary {
1432
+ border-color: hsl(var(--primary));
1433
+ }
1434
+
1435
+ .border-transparent {
1436
+ border-color: transparent;
1437
+ }
1438
+
1439
+ .border-l-transparent {
1440
+ border-left-color: transparent;
1441
+ }
1442
+
1443
+ .border-t-transparent {
1444
+ border-top-color: transparent;
1445
+ }
1446
+
1447
+ .bg-background {
1448
+ background-color: hsl(var(--background));
1449
+ }
1450
+
1451
+ .bg-black {
1452
+ --tw-bg-opacity: 1;
1453
+ background-color: rgb(0 0 0 / var(--tw-bg-opacity));
1454
+ }
1455
+
1456
+ .bg-black\/80 {
1457
+ background-color: rgb(0 0 0 / 0.8);
1458
+ }
1459
+
1460
+ .bg-blue-500 {
1461
+ --tw-bg-opacity: 1;
1462
+ background-color: rgb(59 130 246 / var(--tw-bg-opacity));
1463
+ }
1464
+
1465
+ .bg-border {
1466
+ background-color: hsl(var(--border));
1467
+ }
1468
+
1469
+ .bg-card {
1470
+ background-color: hsl(var(--card));
1471
+ }
1472
+
1473
+ .bg-destructive {
1474
+ background-color: hsl(var(--destructive));
1475
+ }
1476
+
1477
+ .bg-input {
1478
+ background-color: hsl(var(--input));
1479
+ }
1480
+
1481
+ .bg-muted {
1482
+ background-color: hsl(var(--muted));
1483
+ }
1484
+
1485
+ .bg-muted\/50 {
1486
+ background-color: hsl(var(--muted) / 0.5);
1487
+ }
1488
+
1489
+ .bg-popover {
1490
+ background-color: hsl(var(--popover));
1491
+ }
1492
+
1493
+ .bg-primary {
1494
+ background-color: hsl(var(--primary));
1495
+ }
1496
+
1497
+ .bg-red-300 {
1498
+ --tw-bg-opacity: 1;
1499
+ background-color: rgb(252 165 165 / var(--tw-bg-opacity));
1500
+ }
1501
+
1502
+ .bg-red-500 {
1503
+ --tw-bg-opacity: 1;
1504
+ background-color: rgb(239 68 68 / var(--tw-bg-opacity));
1505
+ }
1506
+
1507
+ .bg-secondary {
1508
+ background-color: hsl(var(--secondary));
1509
+ }
1510
+
1511
+ .bg-white {
1512
+ --tw-bg-opacity: 1;
1513
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1514
+ }
1515
+
1516
+ .bg-\[\#0A66C2\] {
1517
+ --tw-bg-opacity: 1;
1518
+ background-color: rgb(10 102 194 / var(--tw-bg-opacity));
1519
+ }
1520
+
1521
+ .bg-gradient-to-r {
1522
+ background-image: linear-gradient(to right, var(--tw-gradient-stops));
1523
+ }
1524
+
1525
+ .bg-gradient-to-t {
1526
+ background-image: linear-gradient(to top, var(--tw-gradient-stops));
1527
+ }
1528
+
1529
+ .from-\[\#fcfcfd\] {
1530
+ --tw-gradient-from: #fcfcfd var(--tw-gradient-from-position);
1531
+ --tw-gradient-to: rgb(252 252 253 / 0) var(--tw-gradient-to-position);
1532
+ --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1533
+ }
1534
+
1535
+ .from-black {
1536
+ --tw-gradient-from: #000 var(--tw-gradient-from-position);
1537
+ --tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);
1538
+ --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
1539
+ }
1540
+
1541
+ .to-slate-700 {
1542
+ --tw-gradient-to: #334155 var(--tw-gradient-to-position);
1543
+ }
1544
+
1545
+ .bg-clip-text {
1546
+ -webkit-background-clip: text;
1547
+ background-clip: text;
1548
+ }
1549
+
1550
+ .fill-current {
1551
+ fill: currentColor;
1552
+ }
1553
+
1554
+ .object-contain {
1555
+ -o-object-fit: contain;
1556
+ object-fit: contain;
1557
+ }
1558
+
1559
+ .p-1 {
1560
+ padding: 0.25rem;
1561
+ }
1562
+
1563
+ .p-10 {
1564
+ padding: 2.5rem;
1565
+ }
1566
+
1567
+ .p-2 {
1568
+ padding: 0.5rem;
1569
+ }
1570
+
1571
+ .p-3 {
1572
+ padding: 0.75rem;
1573
+ }
1574
+
1575
+ .p-4 {
1576
+ padding: 1rem;
1577
+ }
1578
+
1579
+ .p-5 {
1580
+ padding: 1.25rem;
1581
+ }
1582
+
1583
+ .p-6 {
1584
+ padding: 1.5rem;
1585
+ }
1586
+
1587
+ .p-8 {
1588
+ padding: 2rem;
1589
+ }
1590
+
1591
+ .p-\[1px\] {
1592
+ padding: 1px;
1593
+ }
1594
+
1595
+ .px-2 {
1596
+ padding-left: 0.5rem;
1597
+ padding-right: 0.5rem;
1598
+ }
1599
+
1600
+ .px-2\.5 {
1601
+ padding-left: 0.625rem;
1602
+ padding-right: 0.625rem;
1603
+ }
1604
+
1605
+ .px-3 {
1606
+ padding-left: 0.75rem;
1607
+ padding-right: 0.75rem;
1608
+ }
1609
+
1610
+ .px-4 {
1611
+ padding-left: 1rem;
1612
+ padding-right: 1rem;
1613
+ }
1614
+
1615
+ .px-5 {
1616
+ padding-left: 1.25rem;
1617
+ padding-right: 1.25rem;
1618
+ }
1619
+
1620
+ .px-8 {
1621
+ padding-left: 2rem;
1622
+ padding-right: 2rem;
1623
+ }
1624
+
1625
+ .py-0\.5 {
1626
+ padding-top: 0.125rem;
1627
+ padding-bottom: 0.125rem;
1628
+ }
1629
+
1630
+ .py-1 {
1631
+ padding-top: 0.25rem;
1632
+ padding-bottom: 0.25rem;
1633
+ }
1634
+
1635
+ .py-1\.5 {
1636
+ padding-top: 0.375rem;
1637
+ padding-bottom: 0.375rem;
1638
+ }
1639
+
1640
+ .py-2 {
1641
+ padding-top: 0.5rem;
1642
+ padding-bottom: 0.5rem;
1643
+ }
1644
+
1645
+ .py-4 {
1646
+ padding-top: 1rem;
1647
+ padding-bottom: 1rem;
1648
+ }
1649
+
1650
+ .py-5 {
1651
+ padding-top: 1.25rem;
1652
+ padding-bottom: 1.25rem;
1653
+ }
1654
+
1655
+ .pb-4 {
1656
+ padding-bottom: 1rem;
1657
+ }
1658
+
1659
+ .pl-10 {
1660
+ padding-left: 2.5rem;
1661
+ }
1662
+
1663
+ .pl-4 {
1664
+ padding-left: 1rem;
1665
+ }
1666
+
1667
+ .pl-8 {
1668
+ padding-left: 2rem;
1669
+ }
1670
+
1671
+ .pr-2 {
1672
+ padding-right: 0.5rem;
1673
+ }
1674
+
1675
+ .pr-8 {
1676
+ padding-right: 2rem;
1677
+ }
1678
+
1679
+ .pt-0 {
1680
+ padding-top: 0px;
1681
+ }
1682
+
1683
+ .pt-4 {
1684
+ padding-top: 1rem;
1685
+ }
1686
+
1687
+ .pt-\[7\%\] {
1688
+ padding-top: 7%;
1689
+ }
1690
+
1691
+ .text-left {
1692
+ text-align: left;
1693
+ }
1694
+
1695
+ .text-center {
1696
+ text-align: center;
1697
+ }
1698
+
1699
+ .align-middle {
1700
+ vertical-align: middle;
1701
+ }
1702
+
1703
+ .font-mono {
1704
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1705
+ }
1706
+
1707
+ .text-2xl {
1708
+ font-size: 1.5rem;
1709
+ line-height: 2rem;
1710
+ }
1711
+
1712
+ .text-3xl {
1713
+ font-size: 1.875rem;
1714
+ line-height: 2.25rem;
1715
+ }
1716
+
1717
+ .text-5xl {
1718
+ font-size: 3rem;
1719
+ line-height: 1;
1720
+ }
1721
+
1722
+ .text-base {
1723
+ font-size: 1rem;
1724
+ line-height: 1.5rem;
1725
+ }
1726
+
1727
+ .text-lg {
1728
+ font-size: 1.125rem;
1729
+ line-height: 1.75rem;
1730
+ }
1731
+
1732
+ .text-sm {
1733
+ font-size: 0.875rem;
1734
+ line-height: 1.25rem;
1735
+ }
1736
+
1737
+ .text-xl {
1738
+ font-size: 1.25rem;
1739
+ line-height: 1.75rem;
1740
+ }
1741
+
1742
+ .text-xs {
1743
+ font-size: 0.75rem;
1744
+ line-height: 1rem;
1745
+ }
1746
+
1747
+ .font-bold {
1748
+ font-weight: 700;
1749
+ }
1750
+
1751
+ .font-medium {
1752
+ font-weight: 500;
1753
+ }
1754
+
1755
+ .font-normal {
1756
+ font-weight: 400;
1757
+ }
1758
+
1759
+ .font-semibold {
1760
+ font-weight: 600;
1761
+ }
1762
+
1763
+ .capitalize {
1764
+ text-transform: capitalize;
1765
+ }
1766
+
1767
+ .leading-none {
1768
+ line-height: 1;
1769
+ }
1770
+
1771
+ .tracking-tight {
1772
+ letter-spacing: -0.025em;
1773
+ }
1774
+
1775
+ .tracking-wide {
1776
+ letter-spacing: 0.025em;
1777
+ }
1778
+
1779
+ .text-card-foreground {
1780
+ color: hsl(var(--card-foreground));
1781
+ }
1782
+
1783
+ .text-current {
1784
+ color: currentColor;
1785
+ }
1786
+
1787
+ .text-destructive {
1788
+ color: hsl(var(--destructive));
1789
+ }
1790
+
1791
+ .text-destructive-foreground {
1792
+ color: hsl(var(--destructive-foreground));
1793
+ }
1794
+
1795
+ .text-foreground {
1796
+ color: hsl(var(--foreground));
1797
+ }
1798
+
1799
+ .text-foreground\/50 {
1800
+ color: hsl(var(--foreground) / 0.5);
1801
+ }
1802
+
1803
+ .text-gray-800 {
1804
+ --tw-text-opacity: 1;
1805
+ color: rgb(31 41 55 / var(--tw-text-opacity));
1806
+ }
1807
+
1808
+ .text-gray-900 {
1809
+ --tw-text-opacity: 1;
1810
+ color: rgb(17 24 39 / var(--tw-text-opacity));
1811
+ }
1812
+
1813
+ .text-muted-foreground {
1814
+ color: hsl(var(--muted-foreground));
1815
+ }
1816
+
1817
+ .text-popover-foreground {
1818
+ color: hsl(var(--popover-foreground));
1819
+ }
1820
+
1821
+ .text-primary {
1822
+ color: hsl(var(--primary));
1823
+ }
1824
+
1825
+ .text-primary-foreground {
1826
+ color: hsl(var(--primary-foreground));
1827
+ }
1828
+
1829
+ .text-secondary-foreground {
1830
+ color: hsl(var(--secondary-foreground));
1831
+ }
1832
+
1833
+ .text-transparent {
1834
+ color: transparent;
1835
+ }
1836
+
1837
+ .text-white {
1838
+ --tw-text-opacity: 1;
1839
+ color: rgb(255 255 255 / var(--tw-text-opacity));
1840
+ }
1841
+
1842
+ .no-underline {
1843
+ text-decoration-line: none;
1844
+ }
1845
+
1846
+ .underline-offset-4 {
1847
+ text-underline-offset: 4px;
1848
+ }
1849
+
1850
+ .antialiased {
1851
+ -webkit-font-smoothing: antialiased;
1852
+ -moz-osx-font-smoothing: grayscale;
1853
+ }
1854
+
1855
+ .opacity-0 {
1856
+ opacity: 0;
1857
+ }
1858
+
1859
+ .opacity-50 {
1860
+ opacity: 0.5;
1861
+ }
1862
+
1863
+ .opacity-70 {
1864
+ opacity: 0.7;
1865
+ }
1866
+
1867
+ .opacity-90 {
1868
+ opacity: 0.9;
1869
+ }
1870
+
1871
+ .shadow-lg {
1872
+ --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
1873
+ --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
1874
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1875
+ }
1876
+
1877
+ .shadow-md {
1878
+ --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
1879
+ --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
1880
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1881
+ }
1882
+
1883
+ .shadow-sm {
1884
+ --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
1885
+ --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
1886
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1887
+ }
1888
+
1889
+ .outline-none {
1890
+ outline: 2px solid transparent;
1891
+ outline-offset: 2px;
1892
+ }
1893
+
1894
+ .outline {
1895
+ outline-style: solid;
1896
+ }
1897
+
1898
+ .ring {
1899
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1900
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1901
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1902
+ }
1903
+
1904
+ .ring-0 {
1905
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1906
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1907
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1908
+ }
1909
+
1910
+ .ring-1 {
1911
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1912
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1913
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1914
+ }
1915
+
1916
+ .ring-black {
1917
+ --tw-ring-opacity: 1;
1918
+ --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity));
1919
+ }
1920
+
1921
+ .ring-opacity-5 {
1922
+ --tw-ring-opacity: 0.05;
1923
+ }
1924
+
1925
+ .ring-offset-background {
1926
+ --tw-ring-offset-color: hsl(var(--background));
1927
+ }
1928
+
1929
+ .ring-offset-transparent {
1930
+ --tw-ring-offset-color: transparent;
1931
+ }
1932
+
1933
+ .blur {
1934
+ --tw-blur: blur(8px);
1935
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1936
+ }
1937
+
1938
+ .filter {
1939
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1940
+ }
1941
+
1942
+ .backdrop-filter {
1943
+ -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
1944
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
1945
+ }
1946
+
1947
+ .transition {
1948
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
1949
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
1950
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
1951
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1952
+ transition-duration: 150ms;
1953
+ }
1954
+
1955
+ .transition-all {
1956
+ transition-property: all;
1957
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1958
+ transition-duration: 150ms;
1959
+ }
1960
+
1961
+ .transition-colors {
1962
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
1963
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1964
+ transition-duration: 150ms;
1965
+ }
1966
+
1967
+ .transition-opacity {
1968
+ transition-property: opacity;
1969
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1970
+ transition-duration: 150ms;
1971
+ }
1972
+
1973
+ .transition-transform {
1974
+ transition-property: transform;
1975
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1976
+ transition-duration: 150ms;
1977
+ }
1978
+
1979
+ .duration-200 {
1980
+ transition-duration: 200ms;
1981
+ }
1982
+
1983
+ .duration-300 {
1984
+ transition-duration: 300ms;
1985
+ }
1986
+
1987
+ .ease-in-out {
1988
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1989
+ }
1990
+
1991
+ .ease-out {
1992
+ transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
1993
+ }
1994
+
1995
+ @keyframes enter {
1996
+ from {
1997
+ opacity: var(--tw-enter-opacity, 1);
1998
+ transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0));
1999
+ }
2000
+ }
2001
+
2002
+ @keyframes exit {
2003
+ to {
2004
+ opacity: var(--tw-exit-opacity, 1);
2005
+ transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0));
2006
+ }
2007
+ }
2008
+
2009
+ .animate-in {
2010
+ animation-name: enter;
2011
+ animation-duration: 150ms;
2012
+ --tw-enter-opacity: initial;
2013
+ --tw-enter-scale: initial;
2014
+ --tw-enter-rotate: initial;
2015
+ --tw-enter-translate-x: initial;
2016
+ --tw-enter-translate-y: initial;
2017
+ }
2018
+
2019
+ .animate-out {
2020
+ animation-name: exit;
2021
+ animation-duration: 150ms;
2022
+ --tw-exit-opacity: initial;
2023
+ --tw-exit-scale: initial;
2024
+ --tw-exit-rotate: initial;
2025
+ --tw-exit-translate-x: initial;
2026
+ --tw-exit-translate-y: initial;
2027
+ }
2028
+
2029
+ .fade-in {
2030
+ --tw-enter-opacity: 0;
2031
+ }
2032
+
2033
+ .fade-out {
2034
+ --tw-exit-opacity: 0;
2035
+ }
2036
+
2037
+ .zoom-in {
2038
+ --tw-enter-scale: 0;
2039
+ }
2040
+
2041
+ .zoom-out {
2042
+ --tw-exit-scale: 0;
2043
+ }
2044
+
2045
+ .spin-in {
2046
+ --tw-enter-rotate: 30deg;
2047
+ }
2048
+
2049
+ .spin-out {
2050
+ --tw-exit-rotate: 30deg;
2051
+ }
2052
+
2053
+ .slide-in-from-bottom {
2054
+ --tw-enter-translate-y: 100%;
2055
+ }
2056
+
2057
+ .slide-in-from-bottom-2 {
2058
+ --tw-enter-translate-y: 0.5rem;
2059
+ }
2060
+
2061
+ .slide-in-from-left {
2062
+ --tw-enter-translate-x: -100%;
2063
+ }
2064
+
2065
+ .slide-in-from-right {
2066
+ --tw-enter-translate-x: 100%;
2067
+ }
2068
+
2069
+ .slide-in-from-top {
2070
+ --tw-enter-translate-y: -100%;
2071
+ }
2072
+
2073
+ .slide-in-from-top-2 {
2074
+ --tw-enter-translate-y: -0.5rem;
2075
+ }
2076
+
2077
+ .slide-out-to-bottom {
2078
+ --tw-exit-translate-y: 100%;
2079
+ }
2080
+
2081
+ .slide-out-to-left {
2082
+ --tw-exit-translate-x: -100%;
2083
+ }
2084
+
2085
+ .slide-out-to-right {
2086
+ --tw-exit-translate-x: 100%;
2087
+ }
2088
+
2089
+ .slide-out-to-top {
2090
+ --tw-exit-translate-y: -100%;
2091
+ }
2092
+
2093
+ .duration-200 {
2094
+ animation-duration: 200ms;
2095
+ }
2096
+
2097
+ .duration-300 {
2098
+ animation-duration: 300ms;
2099
+ }
2100
+
2101
+ .ease-in-out {
2102
+ animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
2103
+ }
2104
+
2105
+ .ease-out {
2106
+ animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
2107
+ }
2108
+
2109
+ .running {
2110
+ animation-play-state: running;
2111
+ }
2112
+
2113
+ .paused {
2114
+ animation-play-state: paused;
2115
+ }
2116
+
2117
+ /* Hide scrollbar for Chrome, Safari and Opera */
2118
+
2119
+ .no-scrollbar::-webkit-scrollbar {
2120
+ display: none;
2121
+ }
2122
+
2123
+ /* Hide scrollbar for IE, Edge and Firefox */
2124
+
2125
+ .no-scrollbar {
2126
+ -webkit-overflow-scrolling: touch;
2127
+ -ms-overflow-style: none;
2128
+ /* IE and Edge */
2129
+ scrollbar-width: none;
2130
+ /* Firefox */
2131
+ }
2132
+
2133
+ @keyframes slideInFromTop {
2134
+ from {
2135
+ transform: translateY(-100%);
2136
+ }
2137
+
2138
+ to {
2139
+ transform: translateY(0);
2140
+ }
2141
+ }
2142
+
2143
+ @keyframes slideInFromBottom {
2144
+ from {
2145
+ transform: translateY(100%);
2146
+ }
2147
+
2148
+ to {
2149
+ transform: translateY(0);
2150
+ }
2151
+ }
2152
+
2153
+ .toast {
2154
+ animation-duration: 0.2s;
2155
+ animation-fill-mode: forwards;
2156
+ }
2157
+
2158
+ @media (max-width: 640px) {
2159
+ .toast {
2160
+ animation-name: slideInFromTop;
2161
+ }
2162
+ }
2163
+
2164
+ @media (min-width: 641px) {
2165
+ .toast {
2166
+ animation-name: slideInFromBottom;
2167
+ }
2168
+ }
2169
+
2170
+ @keyframes fade-in {
2171
+ from {
2172
+ opacity: 0;
2173
+ }
2174
+
2175
+ to {
2176
+ opacity: 1;
2177
+ }
2178
+ }
2179
+
2180
+ @keyframes slide-up {
2181
+ from {
2182
+ transform: translateY(20px);
2183
+ opacity: 0;
2184
+ }
2185
+
2186
+ to {
2187
+ transform: translateY(0);
2188
+ opacity: 1;
2189
+ }
2190
+ }
2191
+
2192
+ .animate-fade-in {
2193
+ animation: fade-in 1s ease-out forwards;
2194
+ }
2195
+
2196
+ .animate-slide-up {
2197
+ animation: slide-up 1s ease-out forwards;
2198
+ }
2199
+
2200
+ .sim-map-button.active {
2201
+ background-color: #61D790;
2202
+ color: #2E2F27;
2203
+ &:hover {
2204
+ background-color: #61D790;
2205
+ }
2206
+ }
2207
+
2208
+ .text-highlight strong {
2209
+ color: black;
2210
+ .dark & {
2211
+ color: white;
2212
+ }
2213
+ }
2214
+
2215
+ .tokens-button {
2216
+ background-color: #B7E2F1;
2217
+ color: #2E2F27;
2218
+ }
2219
+
2220
+ .overlay-image {
2221
+ opacity: 0.5;
2222
+ position: absolute;
2223
+ top: 0;
2224
+ left: 0;
2225
+ width: 100%;
2226
+ height: 100%;
2227
+ z-index: 10;
2228
+ }
2229
+
2230
+ header {
2231
+ grid-column: 1/-1;
2232
+ }
2233
+
2234
+ body {
2235
+ &[data-is-home="true"] {
2236
+ background: radial-gradient(circle at 50% 100%, #fcfcfd, #fcfcfd, #fdfdfe, #fdfdfe, #fefefe, #fefefe, #ffffff, #ffffff);
2237
+ .dark & {
2238
+ background: radial-gradient(circle at 50% 50%, #272a2d, #242629, #212326, #1e1f22, #1b1c1e, #18181b, #151517, #111113);
2239
+ }
2240
+ }
2241
+ }
2242
+
2243
+ main {
2244
+ overflow: auto;
2245
+ }
2246
+
2247
+ aside {
2248
+ overflow: auto;
2249
+ }
2250
+
2251
+ .scroll-container {
2252
+ padding-right: 10px;
2253
+ }
2254
+
2255
+ .question-message {
2256
+ background-color: #61D790;
2257
+ color: #2E2F27;
2258
+ }
2259
+
2260
+ @media (min-width: 768px) {
2261
+ .grid-image-text-columns {
2262
+ grid-column: span 2 / span 2;
2263
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2264
+ }
2265
+ }
2266
+
2267
+ .grid-image-column {
2268
+ grid-row: span 2 / span 2;
2269
+ display: grid;
2270
+ grid-template-rows: subgrid;
2271
+ align-content: flex-start;
2272
+ }
2273
+
2274
+ @media (min-width: 768px) {
2275
+ .md-grid-text-column {
2276
+ grid-row: span 2 / span 2;
2277
+ display: grid;
2278
+ grid-template-rows: subgrid;
2279
+ align-content: flex-start;
2280
+ }
2281
+ }
2282
+
2283
+ #search-input[aria-expanded="true"] {
2284
+ border-top: 1px solid hsl(var(--input));
2285
+ border-left: 1px solid hsl(var(--input));
2286
+ border-right: 1px solid hsl(var(--input));
2287
+ border-bottom: none;
2288
+ border-bottom-left-radius: 0;
2289
+ border-bottom-right-radius: 0;
2290
+ }
2291
+
2292
+ .awesomplete {
2293
+ width: 100%;
2294
+ }
2295
+
2296
+ .awesomplete > ul > :not([hidden]) ~ :not([hidden]) {
2297
+ --tw-space-y-reverse: 0;
2298
+ margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
2299
+ margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
2300
+ }
2301
+
2302
+ .awesomplete > ul {
2303
+ font-size: 0.875rem;
2304
+ line-height: 1.25rem;
2305
+ margin: 0;
2306
+ border-top: none;
2307
+ border-left: 1px solid hsl(var(--input));
2308
+ border-right: 1px solid hsl(var(--input));
2309
+ border-bottom: 1px solid hsl(var(--input));
2310
+ border-radius: 0 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px);
2311
+ background: white;
2312
+ .dark & {
2313
+ background: hsl(var(--background));
2314
+ }
2315
+ box-shadow: none;
2316
+ text-shadow: none;
2317
+ }
2318
+
2319
+ .awesomplete > ul:before {
2320
+ display: none;
2321
+ }
2322
+
2323
+ .awesomplete > ul > li:hover {
2324
+ background-color: #B7E2F1;
2325
+ color: #2E2F27;
2326
+ }
2327
+
2328
+ .awesomplete > ul > li[aria-selected="true"] {
2329
+ background-color: #B7E2F1;
2330
+ color: #2E2F27;
2331
+ }
2332
+
2333
+ .awesomplete mark {
2334
+ background-color: #61D790;
2335
+ color: #2E2F27;
2336
+ }
2337
+
2338
+ .awesomplete li:hover mark {
2339
+ background-color: #61D790;
2340
+ color: #2E2F27;
2341
+ }
2342
+
2343
+ .awesomplete li[aria-selected="true"] mark {
2344
+ background-color: #61D790;
2345
+ color: #2E2F27;
2346
+ }
2347
+
2348
+ :root:has(.data-\[state\=open\]\:no-bg-scroll[data-state="open"]) {
2349
+ overflow: hidden;
2350
+ }
2351
+
2352
+ :root:has(.group[data-state="open"] .group-data-\[state\=open\]\:no-bg-scroll) {
2353
+ overflow: hidden;
2354
+ }
2355
+
2356
+ .file\:border-0::file-selector-button {
2357
+ border-width: 0px;
2358
+ }
2359
+
2360
+ .file\:bg-transparent::file-selector-button {
2361
+ background-color: transparent;
2362
+ }
2363
+
2364
+ .file\:text-sm::file-selector-button {
2365
+ font-size: 0.875rem;
2366
+ line-height: 1.25rem;
2367
+ }
2368
+
2369
+ .file\:font-medium::file-selector-button {
2370
+ font-weight: 500;
2371
+ }
2372
+
2373
+ .placeholder\:text-muted-foreground::-moz-placeholder {
2374
+ color: hsl(var(--muted-foreground));
2375
+ }
2376
+
2377
+ .placeholder\:text-muted-foreground::placeholder {
2378
+ color: hsl(var(--muted-foreground));
2379
+ }
2380
+
2381
+ .focus-within\:border-input:focus-within {
2382
+ border-color: hsl(var(--input));
2383
+ }
2384
+
2385
+ .focus-within\:outline-none:focus-within {
2386
+ outline: 2px solid transparent;
2387
+ outline-offset: 2px;
2388
+ }
2389
+
2390
+ .focus-within\:ring-2:focus-within {
2391
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
2392
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
2393
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
2394
+ }
2395
+
2396
+ .focus-within\:ring-ring:focus-within {
2397
+ --tw-ring-color: hsl(var(--ring));
2398
+ }
2399
+
2400
+ .focus-within\:ring-offset-2:focus-within {
2401
+ --tw-ring-offset-width: 2px;
2402
+ }
2403
+
2404
+ .hover\:border-black:hover {
2405
+ --tw-border-opacity: 1;
2406
+ border-color: rgb(0 0 0 / var(--tw-border-opacity));
2407
+ }
2408
+
2409
+ .hover\:bg-accent:hover {
2410
+ background-color: hsl(var(--accent));
2411
+ }
2412
+
2413
+ .hover\:bg-destructive\/80:hover {
2414
+ background-color: hsl(var(--destructive) / 0.8);
2415
+ }
2416
+
2417
+ .hover\:bg-destructive\/90:hover {
2418
+ background-color: hsl(var(--destructive) / 0.9);
2419
+ }
2420
+
2421
+ .hover\:bg-muted\/50:hover {
2422
+ background-color: hsl(var(--muted) / 0.5);
2423
+ }
2424
+
2425
+ .hover\:bg-primary\/80:hover {
2426
+ background-color: hsl(var(--primary) / 0.8);
2427
+ }
2428
+
2429
+ .hover\:bg-primary\/90:hover {
2430
+ background-color: hsl(var(--primary) / 0.9);
2431
+ }
2432
+
2433
+ .hover\:bg-secondary\/80:hover {
2434
+ background-color: hsl(var(--secondary) / 0.8);
2435
+ }
2436
+
2437
+ .hover\:text-accent-foreground:hover {
2438
+ color: hsl(var(--accent-foreground));
2439
+ }
2440
+
2441
+ .hover\:text-foreground:hover {
2442
+ color: hsl(var(--foreground));
2443
+ }
2444
+
2445
+ .hover\:text-primary:hover {
2446
+ color: hsl(var(--primary));
2447
+ }
2448
+
2449
+ .hover\:underline:hover {
2450
+ text-decoration-line: underline;
2451
+ }
2452
+
2453
+ .hover\:opacity-100:hover {
2454
+ opacity: 1;
2455
+ }
2456
+
2457
+ .focus\:bg-accent:focus {
2458
+ background-color: hsl(var(--accent));
2459
+ }
2460
+
2461
+ .focus\:text-accent-foreground:focus {
2462
+ color: hsl(var(--accent-foreground));
2463
+ }
2464
+
2465
+ .focus\:opacity-100:focus {
2466
+ opacity: 1;
2467
+ }
2468
+
2469
+ .focus\:outline-none:focus {
2470
+ outline: 2px solid transparent;
2471
+ outline-offset: 2px;
2472
+ }
2473
+
2474
+ .focus\:ring-2:focus {
2475
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
2476
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
2477
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
2478
+ }
2479
+
2480
+ .focus\:ring-ring:focus {
2481
+ --tw-ring-color: hsl(var(--ring));
2482
+ }
2483
+
2484
+ .focus\:ring-offset-2:focus {
2485
+ --tw-ring-offset-width: 2px;
2486
+ }
2487
+
2488
+ .focus-visible\:outline-none:focus-visible {
2489
+ outline: 2px solid transparent;
2490
+ outline-offset: 2px;
2491
+ }
2492
+
2493
+ .focus-visible\:ring-2:focus-visible {
2494
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
2495
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
2496
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
2497
+ }
2498
+
2499
+ .focus-visible\:ring-ring:focus-visible {
2500
+ --tw-ring-color: hsl(var(--ring));
2501
+ }
2502
+
2503
+ .focus-visible\:ring-transparent:focus-visible {
2504
+ --tw-ring-color: transparent;
2505
+ }
2506
+
2507
+ .focus-visible\:ring-offset-2:focus-visible {
2508
+ --tw-ring-offset-width: 2px;
2509
+ }
2510
+
2511
+ .active\:ring:active {
2512
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
2513
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
2514
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
2515
+ }
2516
+
2517
+ .disabled\:pointer-events-none:disabled {
2518
+ pointer-events: none;
2519
+ }
2520
+
2521
+ .disabled\:cursor-not-allowed:disabled {
2522
+ cursor: not-allowed;
2523
+ }
2524
+
2525
+ .disabled\:opacity-50:disabled {
2526
+ opacity: 0.5;
2527
+ }
2528
+
2529
+ .group:hover .group-hover\:opacity-100 {
2530
+ opacity: 1;
2531
+ }
2532
+
2533
+ .group.destructive .group-\[\.destructive\]\:text-red-300 {
2534
+ --tw-text-opacity: 1;
2535
+ color: rgb(252 165 165 / var(--tw-text-opacity));
2536
+ }
2537
+
2538
+ .group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover {
2539
+ --tw-text-opacity: 1;
2540
+ color: rgb(254 242 242 / var(--tw-text-opacity));
2541
+ }
2542
+
2543
+ .group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus {
2544
+ --tw-ring-opacity: 1;
2545
+ --tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity));
2546
+ }
2547
+
2548
+ .group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus {
2549
+ --tw-ring-offset-color: #dc2626;
2550
+ }
2551
+
2552
+ .peer:checked ~ .peer-checked\:bg-primary {
2553
+ background-color: hsl(var(--primary));
2554
+ }
2555
+
2556
+ .peer:checked ~ .peer-checked\:text-primary-foreground {
2557
+ color: hsl(var(--primary-foreground));
2558
+ }
2559
+
2560
+ .peer:disabled ~ .peer-disabled\:cursor-not-allowed {
2561
+ cursor: not-allowed;
2562
+ }
2563
+
2564
+ .peer:disabled ~ .peer-disabled\:opacity-50 {
2565
+ opacity: 0.5;
2566
+ }
2567
+
2568
+ .peer:disabled ~ .peer-disabled\:opacity-70 {
2569
+ opacity: 0.7;
2570
+ }
2571
+
2572
+ .data-\[disabled\]\:pointer-events-none[data-disabled] {
2573
+ pointer-events: none;
2574
+ }
2575
+
2576
+ .data-\[state\=active\]\:bg-background[data-state="active"] {
2577
+ background-color: hsl(var(--background));
2578
+ }
2579
+
2580
+ .data-\[state\=selected\]\:bg-muted[data-state="selected"] {
2581
+ background-color: hsl(var(--muted));
2582
+ }
2583
+
2584
+ .data-\[state\=active\]\:text-foreground[data-state="active"] {
2585
+ color: hsl(var(--foreground));
2586
+ }
2587
+
2588
+ .data-\[disabled\]\:opacity-50[data-disabled] {
2589
+ opacity: 0.5;
2590
+ }
2591
+
2592
+ .data-\[state\=active\]\:shadow-sm[data-state="active"] {
2593
+ --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
2594
+ --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
2595
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
2596
+ }
2597
+
2598
+ .data-\[state\=open\]\:animate-in[data-state="open"] {
2599
+ animation-name: enter;
2600
+ animation-duration: 150ms;
2601
+ --tw-enter-opacity: initial;
2602
+ --tw-enter-scale: initial;
2603
+ --tw-enter-rotate: initial;
2604
+ --tw-enter-translate-x: initial;
2605
+ --tw-enter-translate-y: initial;
2606
+ }
2607
+
2608
+ .data-\[state\=closed\]\:animate-out[data-state="closed"] {
2609
+ animation-name: exit;
2610
+ animation-duration: 150ms;
2611
+ --tw-exit-opacity: initial;
2612
+ --tw-exit-scale: initial;
2613
+ --tw-exit-rotate: initial;
2614
+ --tw-exit-translate-x: initial;
2615
+ --tw-exit-translate-y: initial;
2616
+ }
2617
+
2618
+ .data-\[state\=closed\]\:fade-out-0[data-state="closed"] {
2619
+ --tw-exit-opacity: 0;
2620
+ }
2621
+
2622
+ .data-\[state\=open\]\:fade-in-0[data-state="open"] {
2623
+ --tw-enter-opacity: 0;
2624
+ }
2625
+
2626
+ .data-\[popper-placement\=\'bottom-start\'\]\:slide-in-from-top-2[data-popper-placement='bottom-start'] {
2627
+ --tw-enter-translate-y: -0.5rem;
2628
+ }
2629
+
2630
+ .data-\[popper-placement\=\'top-start\'\]\:slide-in-from-bottom-2[data-popper-placement='top-start'] {
2631
+ --tw-enter-translate-y: 0.5rem;
2632
+ }
2633
+
2634
+ .group[data-checked="true"] .group-data-\[checked\=true\]\:flex {
2635
+ display: flex;
2636
+ }
2637
+
2638
+ .group[data-state="checked"] .group-data-\[state\=checked\]\:flex {
2639
+ display: flex;
2640
+ }
2641
+
2642
+ .group[data-state="open"] .group-data-\[state\=open\]\:rotate-180 {
2643
+ --tw-rotate: 180deg;
2644
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
2645
+ }
2646
+
2647
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:duration-300 {
2648
+ transition-duration: 300ms;
2649
+ }
2650
+
2651
+ .group[data-state="open"] .group-data-\[state\=open\]\:duration-500 {
2652
+ transition-duration: 500ms;
2653
+ }
2654
+
2655
+ .group[data-state="open"] .group-data-\[state\=open\]\:animate-in {
2656
+ animation-name: enter;
2657
+ animation-duration: 150ms;
2658
+ --tw-enter-opacity: initial;
2659
+ --tw-enter-scale: initial;
2660
+ --tw-enter-rotate: initial;
2661
+ --tw-enter-translate-x: initial;
2662
+ --tw-enter-translate-y: initial;
2663
+ }
2664
+
2665
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:animate-out {
2666
+ animation-name: exit;
2667
+ animation-duration: 150ms;
2668
+ --tw-exit-opacity: initial;
2669
+ --tw-exit-scale: initial;
2670
+ --tw-exit-rotate: initial;
2671
+ --tw-exit-translate-x: initial;
2672
+ --tw-exit-translate-y: initial;
2673
+ }
2674
+
2675
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:fade-out-0 {
2676
+ --tw-exit-opacity: 0;
2677
+ }
2678
+
2679
+ .group[data-state="open"] .group-data-\[state\=open\]\:fade-in-0 {
2680
+ --tw-enter-opacity: 0;
2681
+ }
2682
+
2683
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:zoom-out-95 {
2684
+ --tw-exit-scale: .95;
2685
+ }
2686
+
2687
+ .group[data-state="open"] .group-data-\[state\=open\]\:zoom-in-95 {
2688
+ --tw-enter-scale: .95;
2689
+ }
2690
+
2691
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:slide-out-to-bottom {
2692
+ --tw-exit-translate-y: 100%;
2693
+ }
2694
+
2695
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:slide-out-to-left {
2696
+ --tw-exit-translate-x: -100%;
2697
+ }
2698
+
2699
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:slide-out-to-left-1\/2 {
2700
+ --tw-exit-translate-x: -50%;
2701
+ }
2702
+
2703
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:slide-out-to-right {
2704
+ --tw-exit-translate-x: 100%;
2705
+ }
2706
+
2707
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:slide-out-to-top {
2708
+ --tw-exit-translate-y: -100%;
2709
+ }
2710
+
2711
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:slide-out-to-top-\[48\%\] {
2712
+ --tw-exit-translate-y: -48%;
2713
+ }
2714
+
2715
+ .group[data-state="open"] .group-data-\[state\=open\]\:slide-in-from-bottom {
2716
+ --tw-enter-translate-y: 100%;
2717
+ }
2718
+
2719
+ .group[data-state="open"] .group-data-\[state\=open\]\:slide-in-from-left {
2720
+ --tw-enter-translate-x: -100%;
2721
+ }
2722
+
2723
+ .group[data-state="open"] .group-data-\[state\=open\]\:slide-in-from-left-1\/2 {
2724
+ --tw-enter-translate-x: -50%;
2725
+ }
2726
+
2727
+ .group[data-state="open"] .group-data-\[state\=open\]\:slide-in-from-right {
2728
+ --tw-enter-translate-x: 100%;
2729
+ }
2730
+
2731
+ .group[data-state="open"] .group-data-\[state\=open\]\:slide-in-from-top {
2732
+ --tw-enter-translate-y: -100%;
2733
+ }
2734
+
2735
+ .group[data-state="open"] .group-data-\[state\=open\]\:slide-in-from-top-\[48\%\] {
2736
+ --tw-enter-translate-y: -48%;
2737
+ }
2738
+
2739
+ .group[data-state="closed"] .group-data-\[state\=closed\]\:duration-300 {
2740
+ animation-duration: 300ms;
2741
+ }
2742
+
2743
+ .group[data-state="open"] .group-data-\[state\=open\]\:duration-500 {
2744
+ animation-duration: 500ms;
2745
+ }
2746
+
2747
+ .peer[data-state="closed"] ~ .peer-data-\[state\=closed\]\:animate-accordion-up {
2748
+ animation: accordion-up 0.2s ease-out;
2749
+ }
2750
+
2751
+ .peer[data-state="open"] ~ .peer-data-\[state\=open\]\:animate-accordion-down {
2752
+ animation: accordion-down 0.2s ease-out;
2753
+ }
2754
+
2755
+ @media (min-width: 640px) {
2756
+ .sm\:bottom-0 {
2757
+ bottom: 0px;
2758
+ }
2759
+
2760
+ .sm\:right-0 {
2761
+ right: 0px;
2762
+ }
2763
+
2764
+ .sm\:top-auto {
2765
+ top: auto;
2766
+ }
2767
+
2768
+ .sm\:flex {
2769
+ display: flex;
2770
+ }
2771
+
2772
+ .sm\:max-w-sm {
2773
+ max-width: 24rem;
2774
+ }
2775
+
2776
+ .sm\:flex-row {
2777
+ flex-direction: row;
2778
+ }
2779
+
2780
+ .sm\:flex-col {
2781
+ flex-direction: column;
2782
+ }
2783
+
2784
+ .sm\:justify-end {
2785
+ justify-content: flex-end;
2786
+ }
2787
+
2788
+ .sm\:gap-2\.5 {
2789
+ gap: 0.625rem;
2790
+ }
2791
+
2792
+ .sm\:space-x-2 > :not([hidden]) ~ :not([hidden]) {
2793
+ --tw-space-x-reverse: 0;
2794
+ margin-right: calc(0.5rem * var(--tw-space-x-reverse));
2795
+ margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
2796
+ }
2797
+
2798
+ .sm\:rounded-lg {
2799
+ border-radius: var(--radius);
2800
+ }
2801
+
2802
+ .sm\:text-left {
2803
+ text-align: left;
2804
+ }
2805
+ }
2806
+
2807
+ @media (min-width: 768px) {
2808
+ .md\:block {
2809
+ display: block;
2810
+ }
2811
+
2812
+ .md\:max-w-\[420px\] {
2813
+ max-width: 420px;
2814
+ }
2815
+
2816
+ .md\:grid-cols-\[minmax\(0\2c _45fr\)_minmax\(0\2c _15fr\)\] {
2817
+ grid-template-columns: minmax(0, 45fr) minmax(0, 15fr);
2818
+ }
2819
+
2820
+ .md\:text-2xl {
2821
+ font-size: 1.5rem;
2822
+ line-height: 2rem;
2823
+ }
2824
+
2825
+ .md\:text-7xl {
2826
+ font-size: 4.5rem;
2827
+ line-height: 1;
2828
+ }
2829
+
2830
+ .md\:text-6xl {
2831
+ font-size: 3.75rem;
2832
+ line-height: 1;
2833
+ }
2834
+
2835
+ .md\:text-xl {
2836
+ font-size: 1.25rem;
2837
+ line-height: 1.75rem;
2838
+ }
2839
+
2840
+ .md\:tracking-wide {
2841
+ letter-spacing: 0.025em;
2842
+ }
2843
+
2844
+ .md\:tracking-wider {
2845
+ letter-spacing: 0.05em;
2846
+ }
2847
+ }
2848
+
2849
+ @media (min-width: 1024px) {
2850
+ .lg\:space-x-6 > :not([hidden]) ~ :not([hidden]) {
2851
+ --tw-space-x-reverse: 0;
2852
+ margin-right: calc(1.5rem * var(--tw-space-x-reverse));
2853
+ margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse)));
2854
+ }
2855
+ }
2856
+
2857
+ @media (min-width: 1280px) {
2858
+ .xl\:grid-rows-\[1fr_2fr\] {
2859
+ grid-template-rows: 1fr 2fr;
2860
+ }
2861
+ }
2862
+
2863
+ .dark\:block:where(.dark, .dark *) {
2864
+ display: block;
2865
+ }
2866
+
2867
+ .dark\:flex:where(.dark, .dark *) {
2868
+ display: flex;
2869
+ }
2870
+
2871
+ .dark\:hidden:where(.dark, .dark *) {
2872
+ display: none;
2873
+ }
2874
+
2875
+ .dark\:border-destructive:where(.dark, .dark *) {
2876
+ border-color: hsl(var(--destructive));
2877
+ }
2878
+
2879
+ .dark\:bg-background:where(.dark, .dark *) {
2880
+ background-color: hsl(var(--background));
2881
+ }
2882
+
2883
+ .dark\:from-\[\#1c2024\]:where(.dark, .dark *) {
2884
+ --tw-gradient-from: #1c2024 var(--tw-gradient-from-position);
2885
+ --tw-gradient-to: rgb(28 32 36 / 0) var(--tw-gradient-to-position);
2886
+ --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
2887
+ }
2888
+
2889
+ .dark\:from-white:where(.dark, .dark *) {
2890
+ --tw-gradient-from: #fff var(--tw-gradient-from-position);
2891
+ --tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);
2892
+ --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
2893
+ }
2894
+
2895
+ .dark\:to-slate-300:where(.dark, .dark *) {
2896
+ --tw-gradient-to: #cbd5e1 var(--tw-gradient-to-position);
2897
+ }
2898
+
2899
+ .dark\:hover\:border-white:hover:where(.dark, .dark *) {
2900
+ --tw-border-opacity: 1;
2901
+ border-color: rgb(255 255 255 / var(--tw-border-opacity));
2902
+ }
2903
+
2904
+ .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]) {
2905
+ padding-right: 0px;
2906
+ }
2907
+
2908
+ .\[\&\>span\]\:line-clamp-1>span {
2909
+ overflow: hidden;
2910
+ display: -webkit-box;
2911
+ -webkit-box-orient: vertical;
2912
+ -webkit-line-clamp: 1;
2913
+ }
2914
+
2915
+ .\[\&\>span\]\:translate-x-0>span {
2916
+ --tw-translate-x: 0px;
2917
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
2918
+ }
2919
+
2920
+ .peer:checked ~ .peer-checked\:\[\&\>span\]\:flex>span {
2921
+ display: flex;
2922
+ }
2923
+
2924
+ .peer:checked ~ .peer-checked\:\[\&\>span\]\:translate-x-5>span {
2925
+ --tw-translate-x: 1.25rem;
2926
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
2927
+ }
2928
+
2929
+ .\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div {
2930
+ --tw-translate-y: -3px;
2931
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
2932
+ }
2933
+
2934
+ .\[\&\>svg\]\:absolute>svg {
2935
+ position: absolute;
2936
+ }
2937
+
2938
+ .\[\&\>svg\]\:left-4>svg {
2939
+ left: 1rem;
2940
+ }
2941
+
2942
+ .\[\&\>svg\]\:top-4>svg {
2943
+ top: 1rem;
2944
+ }
2945
+
2946
+ .\[\&\>svg\]\:text-destructive>svg {
2947
+ color: hsl(var(--destructive));
2948
+ }
2949
+
2950
+ .\[\&\>svg\]\:text-foreground>svg {
2951
+ color: hsl(var(--foreground));
2952
+ }
2953
+
2954
+ .\[\&\>svg\~\*\]\:pl-7>svg~* {
2955
+ padding-left: 1.75rem;
2956
+ }
2957
+
2958
+ .\[\&\>tr\]\:last\:border-b-0:last-child>tr {
2959
+ border-bottom-width: 0px;
2960
+ }
2961
+
2962
+ .\[\&_p\]\:leading-relaxed p {
2963
+ line-height: 1.625;
2964
+ }
2965
+
2966
+ .\[\&_tr\:last-child\]\:border-0 tr:last-child {
2967
+ border-width: 0px;
2968
+ }
2969
+
2970
+ .\[\&_tr\]\:border-b tr {
2971
+ border-bottom-width: 1px;
2972
+ }
2973
+
requirements.txt ADDED
@@ -0,0 +1,540 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv pip compile pyproject.toml -o src/requirements.txt
3
+ accelerate==0.34.2
4
+ # via peft
5
+ aiohappyeyeballs==2.4.3
6
+ # via aiohttp
7
+ aiohttp==3.10.11
8
+ # via
9
+ # datasets
10
+ # fsspec
11
+ # pyvespa
12
+ aiosignal==1.3.1
13
+ # via aiohttp
14
+ annotated-types==0.7.0
15
+ # via pydantic
16
+ anyio==4.6.0
17
+ # via
18
+ # httpx
19
+ # starlette
20
+ # watchfiles
21
+ async-timeout==4.0.3
22
+ # via aiohttp
23
+ attrs==24.2.0
24
+ # via aiohttp
25
+ beautifulsoup4==4.12.3
26
+ # via python-fasthtml
27
+ blis==0.7.11
28
+ # via thinc
29
+ cachetools==5.5.0
30
+ # via google-auth
31
+ catalogue==2.0.10
32
+ # via
33
+ # spacy
34
+ # srsly
35
+ # thinc
36
+ certifi==2024.8.30
37
+ # via
38
+ # httpcore
39
+ # httpx
40
+ # requests
41
+ cffi==1.17.1
42
+ # via cryptography
43
+ charset-normalizer==3.3.2
44
+ # via requests
45
+ click==8.1.7
46
+ # via
47
+ # typer
48
+ # uvicorn
49
+ cloudpathlib==0.20.0
50
+ # via weasel
51
+ colpali-engine==0.3.1
52
+ # via
53
+ # visual-retrieval-colpali (pyproject.toml)
54
+ # vidore-benchmark
55
+ confection==0.1.5
56
+ # via
57
+ # thinc
58
+ # weasel
59
+ contourpy==1.3.0
60
+ # via matplotlib
61
+ cryptography==43.0.1
62
+ # via pyvespa
63
+ cycler==0.12.1
64
+ # via matplotlib
65
+ cymem==2.0.8
66
+ # via
67
+ # preshed
68
+ # spacy
69
+ # thinc
70
+ datasets==2.21.0
71
+ # via
72
+ # mteb
73
+ # vidore-benchmark
74
+ dill==0.3.8
75
+ # via
76
+ # datasets
77
+ # multiprocess
78
+ docker==7.1.0
79
+ # via pyvespa
80
+ einops==0.8.0
81
+ # via
82
+ # visual-retrieval-colpali (pyproject.toml)
83
+ # vidore-benchmark
84
+ eval-type-backport==0.2.0
85
+ # via mteb
86
+ exceptiongroup==1.2.2
87
+ # via anyio
88
+ fastcore==1.7.11
89
+ # via
90
+ # fastlite
91
+ # python-fasthtml
92
+ # pyvespa
93
+ # sqlite-minutils
94
+ fastlite==0.0.11
95
+ # via python-fasthtml
96
+ filelock==3.16.1
97
+ # via
98
+ # datasets
99
+ # huggingface-hub
100
+ # torch
101
+ # transformers
102
+ fonttools==4.54.1
103
+ # via matplotlib
104
+ frozenlist==1.4.1
105
+ # via
106
+ # aiohttp
107
+ # aiosignal
108
+ fsspec==2024.6.1
109
+ # via
110
+ # datasets
111
+ # huggingface-hub
112
+ # torch
113
+ google-ai-generativelanguage==0.6.10
114
+ # via google-generativeai
115
+ google-api-core==2.21.0
116
+ # via
117
+ # google-ai-generativelanguage
118
+ # google-api-python-client
119
+ # google-generativeai
120
+ google-api-python-client==2.149.0
121
+ # via google-generativeai
122
+ google-auth==2.35.0
123
+ # via
124
+ # google-ai-generativelanguage
125
+ # google-api-core
126
+ # google-api-python-client
127
+ # google-auth-httplib2
128
+ # google-generativeai
129
+ google-auth-httplib2==0.2.0
130
+ # via google-api-python-client
131
+ google-generativeai==0.8.3
132
+ # via visual-retrieval-colpali (pyproject.toml)
133
+ googleapis-common-protos==1.65.0
134
+ # via
135
+ # google-api-core
136
+ # grpcio-status
137
+ gputil==1.4.0
138
+ # via
139
+ # colpali-engine
140
+ # vidore-benchmark
141
+ grpcio==1.67.0
142
+ # via
143
+ # google-api-core
144
+ # grpcio-status
145
+ grpcio-status==1.67.0
146
+ # via google-api-core
147
+ h11==0.14.0
148
+ # via
149
+ # httpcore
150
+ # uvicorn
151
+ h2==4.1.0
152
+ # via httpx
153
+ hpack==4.0.0
154
+ # via h2
155
+ httpcore==1.0.6
156
+ # via httpx
157
+ httplib2==0.22.0
158
+ # via
159
+ # google-api-python-client
160
+ # google-auth-httplib2
161
+ httptools==0.6.1
162
+ # via uvicorn
163
+ httpx==0.27.2
164
+ # via
165
+ # python-fasthtml
166
+ # pyvespa
167
+ huggingface-hub==0.25.1
168
+ # via
169
+ # visual-retrieval-colpali (pyproject.toml)
170
+ # accelerate
171
+ # datasets
172
+ # peft
173
+ # sentence-transformers
174
+ # tokenizers
175
+ # transformers
176
+ hyperframe==6.0.1
177
+ # via h2
178
+ idna==3.10
179
+ # via
180
+ # anyio
181
+ # httpx
182
+ # requests
183
+ # yarl
184
+ itsdangerous==2.2.0
185
+ # via python-fasthtml
186
+ jinja2==3.1.4
187
+ # via
188
+ # pyvespa
189
+ # spacy
190
+ # torch
191
+ joblib==1.4.2
192
+ # via scikit-learn
193
+ kiwisolver==1.4.7
194
+ # via matplotlib
195
+ langcodes==3.4.1
196
+ # via spacy
197
+ language-data==1.2.0
198
+ # via langcodes
199
+ loguru==0.7.2
200
+ # via vidore-benchmark
201
+ lucide-fasthtml==0.0.9
202
+ # via shad4fast
203
+ lxml==5.3.0
204
+ # via
205
+ # lucide-fasthtml
206
+ # pyvespa
207
+ marisa-trie==1.2.1
208
+ # via language-data
209
+ markdown-it-py==3.0.0
210
+ # via rich
211
+ markupsafe==2.1.5
212
+ # via jinja2
213
+ matplotlib==3.9.2
214
+ # via
215
+ # seaborn
216
+ # vidore-benchmark
217
+ mdurl==0.1.2
218
+ # via markdown-it-py
219
+ mpmath==1.3.0
220
+ # via sympy
221
+ mteb==1.15.3
222
+ # via vidore-benchmark
223
+ multidict==6.1.0
224
+ # via
225
+ # aiohttp
226
+ # yarl
227
+ multiprocess==0.70.16
228
+ # via datasets
229
+ murmurhash==1.0.10
230
+ # via
231
+ # preshed
232
+ # spacy
233
+ # thinc
234
+ networkx==3.3
235
+ # via torch
236
+ numpy==1.26.4
237
+ # via
238
+ # accelerate
239
+ # blis
240
+ # colpali-engine
241
+ # contourpy
242
+ # datasets
243
+ # matplotlib
244
+ # mteb
245
+ # pandas
246
+ # peft
247
+ # pyarrow
248
+ # scikit-learn
249
+ # scipy
250
+ # seaborn
251
+ # spacy
252
+ # thinc
253
+ # transformers
254
+ # vidore-benchmark
255
+ oauthlib==3.2.2
256
+ # via python-fasthtml
257
+ packaging==24.1
258
+ # via
259
+ # accelerate
260
+ # datasets
261
+ # fastcore
262
+ # huggingface-hub
263
+ # matplotlib
264
+ # peft
265
+ # spacy
266
+ # thinc
267
+ # transformers
268
+ # weasel
269
+ pandas==2.2.3
270
+ # via
271
+ # datasets
272
+ # seaborn
273
+ pdf2image==1.17.0
274
+ # via vidore-benchmark
275
+ peft==0.11.1
276
+ # via
277
+ # colpali-engine
278
+ # vidore-benchmark
279
+ pillow==10.4.0
280
+ # via
281
+ # colpali-engine
282
+ # matplotlib
283
+ # pdf2image
284
+ # sentence-transformers
285
+ # vidore-benchmark
286
+ pip==24.3.1
287
+ # via visual-retrieval-colpali (pyproject.toml)
288
+ polars==1.9.0
289
+ # via mteb
290
+ preshed==3.0.9
291
+ # via
292
+ # spacy
293
+ # thinc
294
+ proto-plus==1.24.0
295
+ # via
296
+ # google-ai-generativelanguage
297
+ # google-api-core
298
+ protobuf==5.28.3
299
+ # via
300
+ # google-ai-generativelanguage
301
+ # google-api-core
302
+ # google-generativeai
303
+ # googleapis-common-protos
304
+ # grpcio-status
305
+ # proto-plus
306
+ psutil==6.0.0
307
+ # via
308
+ # accelerate
309
+ # peft
310
+ pyarrow==17.0.0
311
+ # via datasets
312
+ pyasn1==0.6.1
313
+ # via
314
+ # pyasn1-modules
315
+ # rsa
316
+ pyasn1-modules==0.4.1
317
+ # via google-auth
318
+ pycparser==2.22
319
+ # via cffi
320
+ pydantic==2.9.2
321
+ # via
322
+ # confection
323
+ # google-generativeai
324
+ # mteb
325
+ # spacy
326
+ # thinc
327
+ # weasel
328
+ pydantic-core==2.23.4
329
+ # via pydantic
330
+ pygments==2.18.0
331
+ # via rich
332
+ pyparsing==3.1.4
333
+ # via
334
+ # httplib2
335
+ # matplotlib
336
+ pypdf==5.0.1
337
+ # via visual-retrieval-colpali (pyproject.toml)
338
+ python-dateutil==2.9.0.post0
339
+ # via
340
+ # matplotlib
341
+ # pandas
342
+ # python-fasthtml
343
+ # pyvespa
344
+ python-dotenv==1.0.1
345
+ # via
346
+ # visual-retrieval-colpali (pyproject.toml)
347
+ # uvicorn
348
+ # vidore-benchmark
349
+ python-fasthtml==0.6.9
350
+ # via
351
+ # visual-retrieval-colpali (pyproject.toml)
352
+ # lucide-fasthtml
353
+ # shad4fast
354
+ python-multipart==0.0.18
355
+ # via python-fasthtml
356
+ pytrec-eval-terrier==0.5.6
357
+ # via mteb
358
+ pytz==2024.2
359
+ # via pandas
360
+ pyvespa==0.50.0
361
+ # via visual-retrieval-colpali (pyproject.toml)
362
+ pyyaml==6.0.2
363
+ # via
364
+ # accelerate
365
+ # datasets
366
+ # huggingface-hub
367
+ # peft
368
+ # transformers
369
+ # uvicorn
370
+ regex==2024.9.11
371
+ # via transformers
372
+ requests==2.32.3
373
+ # via
374
+ # colpali-engine
375
+ # datasets
376
+ # docker
377
+ # google-api-core
378
+ # huggingface-hub
379
+ # lucide-fasthtml
380
+ # mteb
381
+ # pyvespa
382
+ # requests-toolbelt
383
+ # spacy
384
+ # transformers
385
+ # weasel
386
+ requests-toolbelt==1.0.0
387
+ # via pyvespa
388
+ rich==13.9.2
389
+ # via
390
+ # mteb
391
+ # typer
392
+ rsa==4.9
393
+ # via google-auth
394
+ safetensors==0.4.5
395
+ # via
396
+ # accelerate
397
+ # peft
398
+ # transformers
399
+ scikit-learn==1.5.2
400
+ # via
401
+ # mteb
402
+ # sentence-transformers
403
+ scipy==1.14.1
404
+ # via
405
+ # mteb
406
+ # scikit-learn
407
+ # sentence-transformers
408
+ seaborn==0.13.2
409
+ # via vidore-benchmark
410
+ sentence-transformers==3.1.1
411
+ # via
412
+ # mteb
413
+ # vidore-benchmark
414
+ sentencepiece==0.2.0
415
+ # via vidore-benchmark
416
+ setuptools==75.1.0
417
+ # via
418
+ # visual-retrieval-colpali (pyproject.toml)
419
+ # marisa-trie
420
+ # spacy
421
+ # thinc
422
+ shad4fast==1.2.1
423
+ # via visual-retrieval-colpali (pyproject.toml)
424
+ shellingham==1.5.4
425
+ # via typer
426
+ six==1.16.0
427
+ # via python-dateutil
428
+ smart-open==7.0.5
429
+ # via weasel
430
+ sniffio==1.3.1
431
+ # via
432
+ # anyio
433
+ # httpx
434
+ soupsieve==2.6
435
+ # via beautifulsoup4
436
+ spacy==3.7.5
437
+ # via visual-retrieval-colpali (pyproject.toml)
438
+ spacy-legacy==3.0.12
439
+ # via spacy
440
+ spacy-loggers==1.0.5
441
+ # via spacy
442
+ sqlite-minutils==3.37.0.post3
443
+ # via fastlite
444
+ srsly==2.4.8
445
+ # via
446
+ # confection
447
+ # spacy
448
+ # thinc
449
+ # weasel
450
+ starlette==0.39.2
451
+ # via python-fasthtml
452
+ sympy==1.13.3
453
+ # via torch
454
+ tenacity==9.0.0
455
+ # via pyvespa
456
+ thinc==8.2.5
457
+ # via spacy
458
+ threadpoolctl==3.5.0
459
+ # via scikit-learn
460
+ tokenizers==0.20.0
461
+ # via transformers
462
+ torch==2.4.1
463
+ # via
464
+ # visual-retrieval-colpali (pyproject.toml)
465
+ # accelerate
466
+ # colpali-engine
467
+ # mteb
468
+ # peft
469
+ # sentence-transformers
470
+ # vidore-benchmark
471
+ tqdm==4.66.5
472
+ # via
473
+ # datasets
474
+ # google-generativeai
475
+ # huggingface-hub
476
+ # mteb
477
+ # peft
478
+ # sentence-transformers
479
+ # spacy
480
+ # transformers
481
+ transformers==4.45.1
482
+ # via
483
+ # colpali-engine
484
+ # peft
485
+ # sentence-transformers
486
+ # vidore-benchmark
487
+ typer==0.12.5
488
+ # via
489
+ # spacy
490
+ # vidore-benchmark
491
+ # weasel
492
+ typing-extensions==4.12.2
493
+ # via
494
+ # anyio
495
+ # cloudpathlib
496
+ # google-generativeai
497
+ # huggingface-hub
498
+ # mteb
499
+ # multidict
500
+ # pydantic
501
+ # pydantic-core
502
+ # pypdf
503
+ # pyvespa
504
+ # rich
505
+ # torch
506
+ # typer
507
+ # uvicorn
508
+ tzdata==2024.2
509
+ # via pandas
510
+ uritemplate==4.1.1
511
+ # via google-api-python-client
512
+ urllib3==2.2.3
513
+ # via
514
+ # docker
515
+ # requests
516
+ uvicorn==0.31.0
517
+ # via python-fasthtml
518
+ uvloop==0.20.0
519
+ # via uvicorn
520
+ vespacli==8.391.23
521
+ # via visual-retrieval-colpali (pyproject.toml)
522
+ vidore-benchmark==4.0.0
523
+ # via visual-retrieval-colpali (pyproject.toml)
524
+ wasabi==1.1.3
525
+ # via
526
+ # spacy
527
+ # thinc
528
+ # weasel
529
+ watchfiles==0.24.0
530
+ # via uvicorn
531
+ weasel==0.4.1
532
+ # via spacy
533
+ websockets==13.1
534
+ # via uvicorn
535
+ wrapt==1.16.0
536
+ # via smart-open
537
+ xxhash==3.5.0
538
+ # via datasets
539
+ yarl==1.13.1
540
+ # via aiohttp
static/img/colpali_child.png ADDED
static/img/linkedin.svg ADDED
static/img/vespa-colpali.png ADDED
static/img/visual-retrieval-demoapp-arch.png ADDED
static/img/x.svg ADDED
static/js/highlightjs-theme.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function () {
2
+ function getPreferredTheme() {
3
+ if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
4
+ return 'dark';
5
+ }
6
+ return 'light';
7
+ }
8
+
9
+ function syncHighlightTheme() {
10
+ const link = document.getElementById('highlight-theme');
11
+ const preferredTheme = getPreferredTheme();
12
+ link.href = preferredTheme === 'dark' ?
13
+ 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/github-dark.min.css' :
14
+ 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/github.min.css';
15
+ }
16
+
17
+ // Apply the correct theme immediately
18
+ syncHighlightTheme();
19
+
20
+ // Observe changes in the 'dark' class on the <html> element
21
+ const observer = new MutationObserver(syncHighlightTheme);
22
+ observer.observe(document.documentElement, {attributes: true, attributeFilter: ['class']});
23
+ })();
tailwind.config.js ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function filterDefault(values) {
2
+ return Object.fromEntries(
3
+ Object.entries(values).filter(([key]) => key !== "DEFAULT"),
4
+ );
5
+ }
6
+
7
+ /** @type {import('tailwindcss').Config} */
8
+ export default {
9
+ darkMode: ["selector"],
10
+ content: [
11
+ "../**/*.py",
12
+ "../.venv/lib/python3.12/site-packages/shad4fast/**/*.{py,js}",
13
+ ],
14
+ theme: {
15
+ container: {
16
+ center: true,
17
+ padding: "2rem",
18
+ screens: {
19
+ "2xl": "1400px",
20
+ },
21
+ },
22
+ extend: {
23
+ animation: {
24
+ "accordion-down": "accordion-down 0.2s ease-out",
25
+ "accordion-up": "accordion-up 0.2s ease-out",
26
+ },
27
+ animationDelay: ({theme}) => ({
28
+ ...theme("transitionDelay"),
29
+ }),
30
+ animationDuration: ({theme}) => ({
31
+ 0: "0ms",
32
+ ...theme("transitionDuration"),
33
+ }),
34
+ animationTimingFunction: ({theme}) => ({
35
+ ...theme("transitionTimingFunction"),
36
+ }),
37
+ animationFillMode: {
38
+ none: "none",
39
+ forwards: "forwards",
40
+ backwards: "backwards",
41
+ both: "both",
42
+ },
43
+ animationDirection: {
44
+ normal: "normal",
45
+ reverse: "reverse",
46
+ alternate: "alternate",
47
+ "alternate-reverse": "alternate-reverse",
48
+ },
49
+ animationOpacity: ({theme}) => ({
50
+ DEFAULT: 0,
51
+ ...theme("opacity"),
52
+ }),
53
+ animationTranslate: ({theme}) => ({
54
+ DEFAULT: "100%",
55
+ ...theme("translate"),
56
+ }),
57
+ animationScale: ({theme}) => ({
58
+ DEFAULT: 0,
59
+ ...theme("scale"),
60
+ }),
61
+ animationRotate: ({theme}) => ({
62
+ DEFAULT: "30deg",
63
+ ...theme("rotate"),
64
+ }),
65
+ animationRepeat: {
66
+ 0: "0",
67
+ 1: "1",
68
+ infinite: "infinite",
69
+ },
70
+ keyframes: {
71
+ enter: {
72
+ from: {
73
+ opacity: "var(--tw-enter-opacity, 1)",
74
+ transform:
75
+ "translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))",
76
+ },
77
+ },
78
+ exit: {
79
+ to: {
80
+ opacity: "var(--tw-exit-opacity, 1)",
81
+ transform:
82
+ "translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))",
83
+ },
84
+ },
85
+ },
86
+ colors: {
87
+ border: "hsl(var(--border))",
88
+ input: "hsl(var(--input))",
89
+ ring: "hsl(var(--ring))",
90
+ background: "hsl(var(--background))",
91
+ foreground: "hsl(var(--foreground))",
92
+ primary: {
93
+ DEFAULT: "hsl(var(--primary))",
94
+ foreground: "hsl(var(--primary-foreground))",
95
+ },
96
+ secondary: {
97
+ DEFAULT: "hsl(var(--secondary))",
98
+ foreground: "hsl(var(--secondary-foreground))",
99
+ },
100
+ destructive: {
101
+ DEFAULT: "hsl(var(--destructive))",
102
+ foreground: "hsl(var(--destructive-foreground))",
103
+ },
104
+ muted: {
105
+ DEFAULT: "hsl(var(--muted))",
106
+ foreground: "hsl(var(--muted-foreground))",
107
+ },
108
+ accent: {
109
+ DEFAULT: "hsl(var(--accent))",
110
+ foreground: "hsl(var(--accent-foreground))",
111
+ },
112
+ popover: {
113
+ DEFAULT: "hsl(var(--popover))",
114
+ foreground: "hsl(var(--popover-foreground))",
115
+ },
116
+ card: {
117
+ DEFAULT: "hsl(var(--card))",
118
+ foreground: "hsl(var(--card-foreground))",
119
+ },
120
+ },
121
+ borderRadius: {
122
+ lg: `var(--radius)`,
123
+ md: `calc(var(--radius) - 2px)`,
124
+ sm: "calc(var(--radius) - 4px)",
125
+ },
126
+ },
127
+ },
128
+ plugins: [
129
+ function ({addUtilities, matchUtilities, theme}) {
130
+ addUtilities({
131
+ "@keyframes enter": theme("keyframes.enter"),
132
+ "@keyframes exit": theme("keyframes.exit"),
133
+ ".animate-in": {
134
+ animationName: "enter",
135
+ animationDuration: theme("animationDuration.DEFAULT"),
136
+ "--tw-enter-opacity": "initial",
137
+ "--tw-enter-scale": "initial",
138
+ "--tw-enter-rotate": "initial",
139
+ "--tw-enter-translate-x": "initial",
140
+ "--tw-enter-translate-y": "initial",
141
+ },
142
+ ".animate-out": {
143
+ animationName: "exit",
144
+ animationDuration: theme("animationDuration.DEFAULT"),
145
+ "--tw-exit-opacity": "initial",
146
+ "--tw-exit-scale": "initial",
147
+ "--tw-exit-rotate": "initial",
148
+ "--tw-exit-translate-x": "initial",
149
+ "--tw-exit-translate-y": "initial",
150
+ },
151
+ });
152
+
153
+ matchUtilities(
154
+ {
155
+ "fade-in": (value) => ({"--tw-enter-opacity": value}),
156
+ "fade-out": (value) => ({"--tw-exit-opacity": value}),
157
+ },
158
+ {values: theme("animationOpacity")},
159
+ );
160
+
161
+ matchUtilities(
162
+ {
163
+ "zoom-in": (value) => ({"--tw-enter-scale": value}),
164
+ "zoom-out": (value) => ({"--tw-exit-scale": value}),
165
+ },
166
+ {values: theme("animationScale")},
167
+ );
168
+
169
+ matchUtilities(
170
+ {
171
+ "spin-in": (value) => ({"--tw-enter-rotate": value}),
172
+ "spin-out": (value) => ({"--tw-exit-rotate": value}),
173
+ },
174
+ {values: theme("animationRotate")},
175
+ );
176
+
177
+ matchUtilities(
178
+ {
179
+ "slide-in-from-top": (value) => ({
180
+ "--tw-enter-translate-y": `-${value}`,
181
+ }),
182
+ "slide-in-from-bottom": (value) => ({
183
+ "--tw-enter-translate-y": value,
184
+ }),
185
+ "slide-in-from-left": (value) => ({
186
+ "--tw-enter-translate-x": `-${value}`,
187
+ }),
188
+ "slide-in-from-right": (value) => ({
189
+ "--tw-enter-translate-x": value,
190
+ }),
191
+ "slide-out-to-top": (value) => ({
192
+ "--tw-exit-translate-y": `-${value}`,
193
+ }),
194
+ "slide-out-to-bottom": (value) => ({
195
+ "--tw-exit-translate-y": value,
196
+ }),
197
+ "slide-out-to-left": (value) => ({
198
+ "--tw-exit-translate-x": `-${value}`,
199
+ }),
200
+ "slide-out-to-right": (value) => ({
201
+ "--tw-exit-translate-x": value,
202
+ }),
203
+ },
204
+ {values: theme("animationTranslate")},
205
+ );
206
+
207
+ matchUtilities(
208
+ {duration: (value) => ({animationDuration: value})},
209
+ {values: filterDefault(theme("animationDuration"))},
210
+ );
211
+
212
+ matchUtilities(
213
+ {delay: (value) => ({animationDelay: value})},
214
+ {values: theme("animationDelay")},
215
+ );
216
+
217
+ matchUtilities(
218
+ {ease: (value) => ({animationTimingFunction: value})},
219
+ {values: filterDefault(theme("animationTimingFunction"))},
220
+ );
221
+
222
+ addUtilities({
223
+ ".running": {animationPlayState: "running"},
224
+ ".paused": {animationPlayState: "paused"},
225
+ });
226
+
227
+ matchUtilities(
228
+ {"fill-mode": (value) => ({animationFillMode: value})},
229
+ {values: theme("animationFillMode")},
230
+ );
231
+
232
+ matchUtilities(
233
+ {direction: (value) => ({animationDirection: value})},
234
+ {values: theme("animationDirection")},
235
+ );
236
+
237
+ matchUtilities(
238
+ {repeat: (value) => ({animationIterationCount: value})},
239
+ {values: theme("animationRepeat")},
240
+ );
241
+ },
242
+ ],
243
+ };
tailwindcss ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:327703a4646081906e11d116ff4e8e43076466c3d269282bbe612555b9fe0c58
3
+ size 47351504