85 |
86 |
package database
87 |
88 | import (
89 | "errors"
90 |
91 | "github.com/vigneshrajj/gofind/models"
92 | "gorm.io/gorm"
93 | )
94 |
95 | func CreateCommand(db *gorm.DB, command models.Command) error {
96 | if err := db.Create(&command).Error; err != nil {
97 | return err
98 | }
99 | return nil
100 | }
101 |
102 | func FirstOrCreateCommand(db *gorm.DB, command models.Command) {
103 | db.FirstOrCreate(&command)
104 | }
105 |
106 | func DeleteCommand(db *gorm.DB, alias string) error {
107 | if rowsAffected := db.Delete(&models.Command{}, "alias=? AND is_default=?", alias, false).RowsAffected; rowsAffected == 0 {
108 | return errors.New("Command not found")
109 | }
110 | return nil
111 | }
112 |
113 | func ListCommands(db *gorm.DB) []models.Command {
114 | var commands []models.Command
115 | db.Find(&commands)
116 | return commands
117 | }
118 |
119 | func SearchCommand(db *gorm.DB, alias string, partialMatch bool) models.Command {
120 | var command models.Command
121 | if partialMatch {
122 | db.Where("alias LIKE ?", alias+"%").Order("LENGTH(alias) ASC").Find(&command)
123 | } else {
124 | db.Where("alias=?", alias).Find(&command)
125 | }
126 | return command
127 | }
128 |
129 | func GetDefaultCommand(db *gorm.DB) models.Command {
130 | var command models.Command
131 | db.Where("is_default=?", true).Find(&command)
132 | return command
133 | }
134 |
135 | func SetDefaultCommand(db *gorm.DB, alias string) error {
136 | var command models.Command
137 | var defaultCommand models.Command
138 | if db.Where("alias=?", alias).Find(&command); command == (models.Command{}) {
139 | return errors.New("Command not found")
140 | }
141 | if db.Where("is_default=?", true).Find(&defaultCommand); defaultCommand == (models.Command{}) {
142 | return errors.New("Default Command not found")
143 | }
144 | command.IsDefault = true
145 | defaultCommand.IsDefault = false
146 | db.Save(&command)
147 | db.Save(&defaultCommand)
148 | return nil
149 | }
150 |
151 |
152 |
package database
153 |
154 | import (
155 | "database/sql"
156 |
157 | "github.com/vigneshrajj/gofind/models"
158 | "gorm.io/driver/sqlite"
159 | "gorm.io/gorm"
160 | )
161 |
162 | func NewDBConnection(dbFileName string) (*sql.DB, *gorm.DB, error) {
163 | db, err := gorm.Open(sqlite.Open(dbFileName), &gorm.Config{})
164 | if err != nil {
165 | return nil, nil, err
166 | }
167 |
168 | EnsureCommandTableExists(db)
169 |
170 | dbSql, _ := db.DB()
171 | return dbSql, db, nil
172 | }
173 |
174 | func EnsureCommandTableExists(db *gorm.DB) {
175 | db.AutoMigrate(&models.Command{})
176 | }
177 |
178 | func EnsureDefaultCommandsExist(db *gorm.DB) {
179 | defaultCommands := []models.Command{
180 | {
181 | Alias: "g",
182 | Query: "https://www.google.com/search?q=%s",
183 | Type: models.SearchCommand,
184 | Description: sql.NullString{String: "Google Search", Valid: true},
185 | IsDefault: true,
186 | },
187 | {
188 | Alias: "b64",
189 | Query: "",
190 | Type: models.UtilCommand,
191 | Description: sql.NullString{String: "Convert To Base 64 string", Valid: true},
192 | IsDefault: false,
193 | },
194 | {
195 | Alias: "d64",
196 | Query: "",
197 | Type: models.UtilCommand,
198 | Description: sql.NullString{String: "Decode Base 64 string", Valid: true},
199 | IsDefault: false,
200 | },
201 | {
202 | Alias: "sha256",
203 | Query: "",
204 | Type: models.UtilCommand,
205 | Description: sql.NullString{String: "Convert To SHA 256 string", Valid: true},
206 | IsDefault: false,
207 | },
208 | }
209 | for _, command := range defaultCommands {
210 | FirstOrCreateCommand(db, command)
211 | }
212 | }
213 |
214 | func EnsureAdditionalCommandsExist(db *gorm.DB) {
215 | additionalCommands := []models.Command{
216 | {
217 | Alias: "y",
218 | Query: "https://www.youtube.com/results?search_query=%s",
219 | Type: models.SearchCommand,
220 | Description: sql.NullString{String: "Youtube", Valid: true},
221 | IsDefault: false,
222 | },
223 | {
224 | Alias: "ddg",
225 | Query: "https://duckduckgo.com/?q=%s",
226 | Type: models.SearchCommand,
227 | Description: sql.NullString{String: "DuckDuckGo", Valid: true},
228 | IsDefault: false,
229 | },
230 | {
231 | Alias: "ddl",
232 | Query: "https://lite.duckduckgo.com/lite/?q=%s",
233 | Type: models.SearchCommand,
234 | Description: sql.NullString{String: "DuckDuckGo Lite", Valid: true},
235 | IsDefault: false,
236 | },
237 | {
238 | Alias: "gh",
239 | Query: "https://github.com/search?q=%s&type=repositories",
240 | Type: models.SearchCommand,
241 | Description: sql.NullString{String: "Github Repos", Valid: true},
242 | IsDefault: false,
243 | },
244 | {
245 | Alias: "npm",
246 | Query: "https://www.npmjs.com/search?q=%s",
247 | Type: models.SearchCommand,
248 | Description: sql.NullString{String: "Node Package Manager (NPM)", Valid: true},
249 | IsDefault: false,
250 | },
251 | {
252 | Alias: "m",
253 | Query: "https://mail.google.com/mail/u/{r:0,vr:1}/#inbox",
254 | Type: models.SearchCommand,
255 | Description: sql.NullString{String: "GMail", Valid: true},
256 | IsDefault: false,
257 | },
258 | }
259 | for _, command := range additionalCommands {
260 | FirstOrCreateCommand(db, command)
261 | }
262 | }
263 |
264 |
265 |
package handlers
266 |
267 | import (
268 | "database/sql"
269 | "fmt"
270 | "github.com/vigneshrajj/gofind/internal/database"
271 | "github.com/vigneshrajj/gofind/internal/templates"
272 | "github.com/vigneshrajj/gofind/models"
273 | "net/http"
274 | "strings"
275 |
276 | "gorm.io/gorm"
277 | )
278 |
279 | func HandleAddCommand(w http.ResponseWriter, data []string, db *gorm.DB) {
280 | if len(data) < 3 {
281 | w.WriteHeader(http.StatusBadRequest)
282 | templates.MessageTemplate(w, "Invalid number of arguments provided. Add command usage:\n#a <alias> <url-with-args> <description(optional)>")
283 | return
284 | }
285 |
286 | command := models.Command{
287 | Alias: data[1],
288 | Query: data[2],
289 | }
290 | if len(data) > 3 {
291 | command.Description = sql.NullString{String: strings.Join(data[3:], " "), Valid: true}
292 | }
293 |
294 | err := database.CreateCommand(db, command)
295 | if err != nil {
296 | w.WriteHeader(http.StatusBadRequest)
297 | templates.MessageTemplate(w, err.Error())
298 | return
299 | }
300 | templates.MessageTemplate(w, "Added Command: "+data[1]+", URL: "+data[2])
301 | }
302 |
303 | func HandleListCommands(w http.ResponseWriter, data []string, db *gorm.DB) {
304 | if len(data) != 1 {
305 | w.WriteHeader(http.StatusBadRequest)
306 | templates.MessageTemplate(w, "Invalid number of arguments provided. List command usage: #l")
307 | return
308 | }
309 | commands := database.ListCommands(db)
310 | templates.ListCommandsTemplate(w, commands)
311 | }
312 |
313 | func ChangeDefaultCommand(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
314 | alias := r.URL.Query().Get("default")
315 | response := "Default Command has been changed successfully to " + alias
316 | err := database.SetDefaultCommand(db, alias)
317 | if err != nil {
318 | w.WriteHeader(http.StatusBadRequest)
319 | response = "Error setting default command."
320 | }
321 | w.Write([]byte(response))
322 | }
323 |
324 | func HandleDeleteCommand(w http.ResponseWriter, data []string, db *gorm.DB) {
325 | if len(data) != 2 {
326 | w.WriteHeader(http.StatusBadRequest)
327 | templates.MessageTemplate(w, "Invalid number of arguments provided. Delete command usage: #d <alias>")
328 | return
329 | }
330 | command := database.SearchCommand(db, data[1], false)
331 | if command == (models.Command{}) {
332 | w.WriteHeader(http.StatusBadRequest)
333 | templates.MessageTemplate(w, "Command not found.")
334 | return
335 | }
336 | if command.Type == models.ApiCommand || command.Type == models.UtilCommand {
337 | w.WriteHeader(http.StatusBadRequest)
338 | templates.MessageTemplate(w, "Cannot delete built-in utilities or api commands.")
339 | return
340 | }
341 | database.DeleteCommand(db, data[1])
342 | fmt.Fprintf(w, "Deleted Command: %s", data[1])
343 | }
344 |
345 |
346 |
package handlers
347 |
348 | import (
349 | "errors"
350 | "fmt"
351 | "net/http"
352 | "net/url"
353 | "regexp"
354 | "strings"
355 |
356 | "github.com/vigneshrajj/gofind/internal/database"
357 | "github.com/vigneshrajj/gofind/internal/templates"
358 |
359 | "github.com/vigneshrajj/gofind/models"
360 | "gorm.io/gorm"
361 | )
362 |
363 | func isKeyValueArg(query string) bool {
364 | bracketIndex := strings.Index(query, "{")
365 | if bracketIndex == -1 {
366 | return false
367 | }
368 | colonIndex := strings.Index(query[bracketIndex:], ":")
369 | if colonIndex == -1 {
370 | return false
371 | }
372 | return true
373 | }
374 |
375 | func replaceKeyWithValue(input string, choice string) (string, error) {
376 | re := regexp.MustCompile(`{([^}]*)}`)
377 | matches := re.FindStringSubmatch(input)
378 |
379 | content := matches[1]
380 | keyValuePairs := strings.Split(content, ",")
381 |
382 | kvMap := make(map[string]string)
383 | for _, pair := range keyValuePairs {
384 | parts := strings.SplitN(pair, ":", 2)
385 | if len(parts) == 2 {
386 | kvMap[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
387 | }
388 | }
389 |
390 | if value, found := kvMap[choice]; found {
391 | return strings.Replace(input, matches[0], value, 1), nil
392 | } else {
393 | return input, errors.New("Invalid Argument Provided.")
394 | }
395 | }
396 |
397 | func HandleRedirectQuery(w http.ResponseWriter, r *http.Request, data []string, db *gorm.DB) {
398 | alias := data[0]
399 | command := database.SearchCommand(db, alias, true)
400 |
401 | if command == (models.Command{}) {
402 | defaultCommand := database.GetDefaultCommand(db)
403 | command = defaultCommand
404 | data = append([]string{command.Alias}, data...)
405 | }
406 |
407 | if command.Type == models.UtilCommand {
408 | HandleUtilCommand(w, data)
409 | return
410 | }
411 |
412 | query := command.Query
413 |
414 | startsWithHttp := strings.HasPrefix(query, "http://") || strings.HasPrefix(query, "https://")
415 | if !startsWithHttp {
416 | query = "https://" + query
417 | }
418 |
419 | if strings.Contains(query, "%s") {
420 | query = fmt.Sprintf(query, url.QueryEscape(strings.Join(data[1:], " ")))
421 | http.Redirect(w, r, query, http.StatusFound)
422 | return
423 | }
424 |
425 | if isKeyValueArg(query) {
426 | argsCount := strings.Count(query, "{")
427 | inputArgsCount := len(data) - 1
428 | if argsCount != inputArgsCount {
429 | w.WriteHeader(http.StatusBadRequest)
430 | templates.MessageTemplate(w, "Invalid arguments provided")
431 | return
432 | }
433 |
434 | for i := 1; i < len(data); i++ {
435 | var err error
436 | query, err = replaceKeyWithValue(query, data[i])
437 | if err != nil {
438 | w.WriteHeader(http.StatusBadRequest)
439 | templates.MessageTemplate(w, err.Error())
440 | return
441 | }
442 | }
443 | http.Redirect(w, r, query, http.StatusFound)
444 | return
445 | }
446 |
447 | argCount := len(data) - 1
448 | for i := argCount; i >= 1; i-- {
449 | query = strings.Replace(query, fmt.Sprintf("{%d}", i), data[i], -1)
450 | query = strings.Replace(query, fmt.Sprintf("{%d}", i), data[i], -1)
451 | }
452 |
453 | argCountInQuery := strings.Count(query, "{")
454 | isNArgQuery := strings.Count(query, "%s") == 1
455 | if argCountInQuery > 0 && !isNArgQuery {
456 | w.WriteHeader(http.StatusBadRequest)
457 | templates.MessageTemplate(w, "Invalid number of arguments provided")
458 | return
459 | }
460 |
461 | http.Redirect(w, r, query, http.StatusFound)
462 | }
463 |
464 | func HandleQuery(w http.ResponseWriter, r *http.Request, query string, db *gorm.DB) {
465 | if query == "" {
466 | w.WriteHeader(http.StatusBadRequest)
467 | templates.MessageTemplate(w, "Query cannot be empty")
468 | return
469 | }
470 | data := strings.Split(query, " ")
471 | switch data[0] {
472 | case "#a":
473 | HandleAddCommand(w, data, db)
474 | case "#d":
475 | HandleDeleteCommand(w, data, db)
476 | case "#l":
477 | HandleListCommands(w, data, db)
478 | default:
479 | HandleRedirectQuery(w, r, data, db)
480 | }
481 | }
482 |
483 |
484 |
package handlers
485 |
486 | import (
487 | "net/http"
488 |
489 | "github.com/vigneshrajj/gofind/internal/helpers"
490 | "github.com/vigneshrajj/gofind/internal/templates"
491 | )
492 |
493 | func HandleUtilCommand(w http.ResponseWriter, data []string) {
494 | alias := data[0]
495 | switch alias {
496 | case "b64":
497 | HandleB64Util(w, data)
498 | case "d64":
499 | HandleD64Util(w, data)
500 | case "sha256":
501 | HandleSha256Util(w, data)
502 | }
503 | }
504 |
505 | func HandleSha256Util(w http.ResponseWriter, data []string) {
506 | if len(data) != 2 {
507 | w.WriteHeader(http.StatusBadRequest)
508 | templates.MessageTemplate(w, "Invalid number of arguments provided. SHA256 encode command usage: sha256 <string>")
509 | return
510 | }
511 | encoded := helpers.Sha256(data[1])
512 | templates.Sha256Template(w, encoded)
513 | }
514 |
515 | func HandleD64Util(w http.ResponseWriter, data []string) {
516 | if len(data) != 2 {
517 | w.WriteHeader(http.StatusBadRequest)
518 | templates.MessageTemplate(w, "Invalid number of arguments provided. Base64 Decode command usage: d64 <string>")
519 | return
520 | }
521 | decoded := helpers.GetB64Decode(data[1])
522 | templates.Base64DecodeTemplate(w, decoded)
523 | }
524 |
525 | func HandleB64Util(w http.ResponseWriter, data []string) {
526 | if len(data) != 2 {
527 | w.WriteHeader(http.StatusBadRequest)
528 | templates.MessageTemplate(w, "Invalid number of arguments provided. Base64 command usage: b64 <string>")
529 | return
530 | }
531 | encoded := helpers.GetB64(data[1])
532 | templates.Base64Template(w, encoded)
533 | }
534 |
535 |
536 |
package helpers
537 |
538 | import (
539 | "crypto/sha256"
540 | "encoding/base64"
541 | "encoding/hex"
542 | )
543 |
544 | func GetB64(data string) string {
545 | return base64.StdEncoding.EncodeToString([]byte(data))
546 | }
547 |
548 | func GetB64Decode(data string) string {
549 | decoded, _ := base64.StdEncoding.DecodeString(data)
550 | return string(decoded)
551 | }
552 |
553 | func Sha256(data string) string {
554 | hash := sha256.New()
555 | hash.Write([]byte(data))
556 | hashedBytes := hash.Sum(nil)
557 | hashedHex := hex.EncodeToString(hashedBytes)
558 | return hashedHex
559 | }
560 |
561 |
562 |
package server
563 |
564 | import (
565 | "fmt"
566 | "log"
567 | "net/http"
568 |
569 | "github.com/vigneshrajj/gofind/config"
570 | "github.com/vigneshrajj/gofind/internal/database"
571 | "github.com/vigneshrajj/gofind/internal/handlers"
572 | "gorm.io/gorm"
573 | )
574 |
575 | func HandleRoutes(db *gorm.DB) {
576 | fs := http.FileServer(http.Dir("./static"))
577 | http.Handle("/static/", http.StripPrefix("/static/", fs))
578 | http.HandleFunc("/search", func(w http.ResponseWriter, r *http.Request) {
579 | query := r.URL.Query().Get("query")
580 | handlers.HandleQuery(w, r, query, db)
581 | })
582 | http.HandleFunc("/set-default-command", func(w http.ResponseWriter, r *http.Request) {
583 | handlers.ChangeDefaultCommand(w, r, db)
584 | })
585 |
586 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
587 | fmt.Fprintf(w, "Server running on %s", config.Port)
588 | })
589 | }
590 |
591 | func StartServer(DbPath string, Port string) error {
592 | _, db, err := database.NewDBConnection(DbPath)
593 | if err != nil {
594 | return err
595 | }
596 |
597 | database.EnsureDefaultCommandsExist(db)
598 |
599 | if config.EnableAdditionalCommands {
600 | database.EnsureAdditionalCommandsExist(db)
601 | }
602 |
603 | HandleRoutes(db)
604 |
605 | log.Printf("Starting server on %s", Port)
606 | log.Fatal(http.ListenAndServe(Port, nil))
607 | return nil
608 | }
609 |
610 |
611 |
612 |
package templates
613 |
614 | import (
615 | "github.com/vigneshrajj/gofind/models"
616 | "html/template"
617 | "net/http"
618 | )
619 |
620 | type ListCommandsPageData struct {
621 | GroupedCommands map[models.CommandType][]models.Command
622 | }
623 |
624 | func groupByType(commands []models.Command) map[models.CommandType][]models.Command {
625 | groupedCommands := make(map[models.CommandType][]models.Command)
626 |
627 | for _, command := range commands {
628 | groupedCommands[command.Type] = append(groupedCommands[command.Type], command)
629 | }
630 |
631 | return groupedCommands
632 | }
633 |
634 | func ListCommandsTemplate(w http.ResponseWriter, commands []models.Command) {
635 | data := ListCommandsPageData{
636 | GroupedCommands: groupByType(commands),
637 | }
638 | tmpl := template.Must(template.ParseFiles("static/templates/list_commands.html"))
639 | tmpl.Execute(w, data)
640 | }
641 |
642 | type MessagePageData struct {
643 | Message string
644 | }
645 |
646 | func MessageTemplate(w http.ResponseWriter, msg string) {
647 | data := MessagePageData{
648 | Message: msg,
649 | }
650 | tmpl := template.Must(template.ParseFiles("static/templates/message.html"))
651 | tmpl.Execute(w, data)
652 | }
653 |
654 | type B64PageType string
655 |
656 | const (
657 | Encoded B64PageType = "encoded"
658 | Decoded B64PageType = "decoded"
659 | )
660 |
661 | type B64PageData struct {
662 | Value string
663 | Type B64PageType
664 | }
665 |
666 | func Base64Template(w http.ResponseWriter, encoded string) {
667 | data := B64PageData{
668 | Value: encoded,
669 | Type: Encoded,
670 | }
671 | tmpl := template.Must(template.ParseFiles("static/templates/base64.html"))
672 | tmpl.Execute(w, data)
673 | }
674 |
675 | func Base64DecodeTemplate(w http.ResponseWriter, decoded string) {
676 | data := B64PageData{
677 | Value: decoded,
678 | Type: Decoded,
679 | }
680 | tmpl := template.Must(template.ParseFiles("static/templates/base64.html"))
681 | tmpl.Execute(w, data)
682 | }
683 |
684 | type Sha256PageData struct {
685 | Value string
686 | }
687 |
688 | func Sha256Template(w http.ResponseWriter, hashed string) {
689 | data := Sha256PageData{
690 | Value: hashed,
691 | }
692 | tmpl := template.Must(template.ParseFiles("static/templates/sha256.html"))
693 | tmpl.Execute(w, data)
694 | }
695 |
696 |
697 |
698 |
699 |
726 |
727 |
--------------------------------------------------------------------------------
/static/css/styles.css:
--------------------------------------------------------------------------------
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.14 | 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]:where(:not([hidden="until-found"])) {
554 | display: none;
555 | }
556 |
557 | [type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
558 | -webkit-appearance: none;
559 | -moz-appearance: none;
560 | appearance: none;
561 | background-color: #fff;
562 | border-color: #6b7280;
563 | border-width: 1px;
564 | border-radius: 0px;
565 | padding-top: 0.5rem;
566 | padding-right: 0.75rem;
567 | padding-bottom: 0.5rem;
568 | padding-left: 0.75rem;
569 | font-size: 1rem;
570 | line-height: 1.5rem;
571 | --tw-shadow: 0 0 #0000;
572 | }
573 |
574 | [type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
575 | outline: 2px solid transparent;
576 | outline-offset: 2px;
577 | --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
578 | --tw-ring-offset-width: 0px;
579 | --tw-ring-offset-color: #fff;
580 | --tw-ring-color: #2563eb;
581 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
582 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
583 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
584 | border-color: #2563eb;
585 | }
586 |
587 | input::-moz-placeholder, textarea::-moz-placeholder {
588 | color: #6b7280;
589 | opacity: 1;
590 | }
591 |
592 | input::placeholder,textarea::placeholder {
593 | color: #6b7280;
594 | opacity: 1;
595 | }
596 |
597 | ::-webkit-datetime-edit-fields-wrapper {
598 | padding: 0;
599 | }
600 |
601 | ::-webkit-date-and-time-value {
602 | min-height: 1.5em;
603 | text-align: inherit;
604 | }
605 |
606 | ::-webkit-datetime-edit {
607 | display: inline-flex;
608 | }
609 |
610 | ::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
611 | padding-top: 0;
612 | padding-bottom: 0;
613 | }
614 |
615 | select {
616 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
617 | background-position: right 0.5rem center;
618 | background-repeat: no-repeat;
619 | background-size: 1.5em 1.5em;
620 | padding-right: 2.5rem;
621 | -webkit-print-color-adjust: exact;
622 | print-color-adjust: exact;
623 | }
624 |
625 | [multiple],[size]:where(select:not([size="1"])) {
626 | background-image: initial;
627 | background-position: initial;
628 | background-repeat: unset;
629 | background-size: initial;
630 | padding-right: 0.75rem;
631 | -webkit-print-color-adjust: unset;
632 | print-color-adjust: unset;
633 | }
634 |
635 | [type='checkbox'],[type='radio'] {
636 | -webkit-appearance: none;
637 | -moz-appearance: none;
638 | appearance: none;
639 | padding: 0;
640 | -webkit-print-color-adjust: exact;
641 | print-color-adjust: exact;
642 | display: inline-block;
643 | vertical-align: middle;
644 | background-origin: border-box;
645 | -webkit-user-select: none;
646 | -moz-user-select: none;
647 | user-select: none;
648 | flex-shrink: 0;
649 | height: 1rem;
650 | width: 1rem;
651 | color: #2563eb;
652 | background-color: #fff;
653 | border-color: #6b7280;
654 | border-width: 1px;
655 | --tw-shadow: 0 0 #0000;
656 | }
657 |
658 | [type='checkbox'] {
659 | border-radius: 0px;
660 | }
661 |
662 | [type='radio'] {
663 | border-radius: 100%;
664 | }
665 |
666 | [type='checkbox']:focus,[type='radio']:focus {
667 | outline: 2px solid transparent;
668 | outline-offset: 2px;
669 | --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
670 | --tw-ring-offset-width: 2px;
671 | --tw-ring-offset-color: #fff;
672 | --tw-ring-color: #2563eb;
673 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
674 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
675 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
676 | }
677 |
678 | [type='checkbox']:checked,[type='radio']:checked {
679 | border-color: transparent;
680 | background-color: currentColor;
681 | background-size: 100% 100%;
682 | background-position: center;
683 | background-repeat: no-repeat;
684 | }
685 |
686 | [type='checkbox']:checked {
687 | background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
688 | }
689 |
690 | @media (forced-colors: active) {
691 | [type='checkbox']:checked {
692 | -webkit-appearance: auto;
693 | -moz-appearance: auto;
694 | appearance: auto;
695 | }
696 | }
697 |
698 | [type='radio']:checked {
699 | background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
700 | }
701 |
702 | @media (forced-colors: active) {
703 | [type='radio']:checked {
704 | -webkit-appearance: auto;
705 | -moz-appearance: auto;
706 | appearance: auto;
707 | }
708 | }
709 |
710 | [type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
711 | border-color: transparent;
712 | background-color: currentColor;
713 | }
714 |
715 | [type='checkbox']:indeterminate {
716 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
717 | border-color: transparent;
718 | background-color: currentColor;
719 | background-size: 100% 100%;
720 | background-position: center;
721 | background-repeat: no-repeat;
722 | }
723 |
724 | @media (forced-colors: active) {
725 | [type='checkbox']:indeterminate {
726 | -webkit-appearance: auto;
727 | -moz-appearance: auto;
728 | appearance: auto;
729 | }
730 | }
731 |
732 | [type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
733 | border-color: transparent;
734 | background-color: currentColor;
735 | }
736 |
737 | [type='file'] {
738 | background: unset;
739 | border-color: inherit;
740 | border-width: 0;
741 | border-radius: 0;
742 | padding: 0;
743 | font-size: unset;
744 | line-height: inherit;
745 | }
746 |
747 | [type='file']:focus {
748 | outline: 1px solid ButtonText;
749 | outline: 1px auto -webkit-focus-ring-color;
750 | }
751 |
752 | .sr-only {
753 | position: absolute;
754 | width: 1px;
755 | height: 1px;
756 | padding: 0;
757 | margin: -1px;
758 | overflow: hidden;
759 | clip: rect(0, 0, 0, 0);
760 | white-space: nowrap;
761 | border-width: 0;
762 | }
763 |
764 | .pointer-events-none {
765 | pointer-events: none;
766 | }
767 |
768 | .pointer-events-auto {
769 | pointer-events: auto;
770 | }
771 |
772 | .invisible {
773 | visibility: hidden;
774 | }
775 |
776 | .fixed {
777 | position: fixed;
778 | }
779 |
780 | .absolute {
781 | position: absolute;
782 | }
783 |
784 | .relative {
785 | position: relative;
786 | }
787 |
788 | .sticky {
789 | position: sticky;
790 | }
791 |
792 | .inset-0 {
793 | inset: 0px;
794 | }
795 |
796 | .inset-y-0 {
797 | top: 0px;
798 | bottom: 0px;
799 | }
800 |
801 | .bottom-\[calc\(80\%\)\] {
802 | bottom: calc(80%);
803 | }
804 |
805 | .left-0 {
806 | left: 0px;
807 | }
808 |
809 | .right-0 {
810 | right: 0px;
811 | }
812 |
813 | .top-0 {
814 | top: 0px;
815 | }
816 |
817 | .z-10 {
818 | z-index: 10;
819 | }
820 |
821 | .-mx-4 {
822 | margin-left: -1rem;
823 | margin-right: -1rem;
824 | }
825 |
826 | .-my-2 {
827 | margin-top: -0.5rem;
828 | margin-bottom: -0.5rem;
829 | }
830 |
831 | .mx-auto {
832 | margin-left: auto;
833 | margin-right: auto;
834 | }
835 |
836 | .my-1 {
837 | margin-top: 0.25rem;
838 | margin-bottom: 0.25rem;
839 | }
840 |
841 | .my-10 {
842 | margin-top: 2.5rem;
843 | margin-bottom: 2.5rem;
844 | }
845 |
846 | .my-4 {
847 | margin-top: 1rem;
848 | margin-bottom: 1rem;
849 | }
850 |
851 | .-mb-px {
852 | margin-bottom: -1px;
853 | }
854 |
855 | .ml-3 {
856 | margin-left: 0.75rem;
857 | }
858 |
859 | .ml-4 {
860 | margin-left: 1rem;
861 | }
862 |
863 | .mt-2 {
864 | margin-top: 0.5rem;
865 | }
866 |
867 | .block {
868 | display: block;
869 | }
870 |
871 | .inline-block {
872 | display: inline-block;
873 | }
874 |
875 | .flex {
876 | display: flex;
877 | }
878 |
879 | .inline-flex {
880 | display: inline-flex;
881 | }
882 |
883 | .table {
884 | display: table;
885 | }
886 |
887 | .table-cell {
888 | display: table-cell;
889 | }
890 |
891 | .flow-root {
892 | display: flow-root;
893 | }
894 |
895 | .hidden {
896 | display: none;
897 | }
898 |
899 | .h-4 {
900 | height: 1rem;
901 | }
902 |
903 | .h-5 {
904 | height: 1.25rem;
905 | }
906 |
907 | .h-6 {
908 | height: 1.5rem;
909 | }
910 |
911 | .w-0 {
912 | width: 0px;
913 | }
914 |
915 | .w-10 {
916 | width: 2.5rem;
917 | }
918 |
919 | .w-4 {
920 | width: 1rem;
921 | }
922 |
923 | .w-5 {
924 | width: 1.25rem;
925 | }
926 |
927 | .w-6 {
928 | width: 1.5rem;
929 | }
930 |
931 | .w-full {
932 | width: 100%;
933 | }
934 |
935 | .min-w-full {
936 | min-width: 100%;
937 | }
938 |
939 | .max-w-7xl {
940 | max-width: 80rem;
941 | }
942 |
943 | .max-w-\[20vw\] {
944 | max-width: 20vw;
945 | }
946 |
947 | .max-w-sm {
948 | max-width: 24rem;
949 | }
950 |
951 | .flex-1 {
952 | flex: 1 1 0%;
953 | }
954 |
955 | .shrink-0 {
956 | flex-shrink: 0;
957 | }
958 |
959 | .table-fixed {
960 | table-layout: fixed;
961 | }
962 |
963 | .border-separate {
964 | border-collapse: separate;
965 | }
966 |
967 | .border-spacing-0 {
968 | --tw-border-spacing-x: 0px;
969 | --tw-border-spacing-y: 0px;
970 | border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y);
971 | }
972 |
973 | .flex-col {
974 | flex-direction: column;
975 | }
976 |
977 | .items-start {
978 | align-items: flex-start;
979 | }
980 |
981 | .items-end {
982 | align-items: flex-end;
983 | }
984 |
985 | .items-center {
986 | align-items: center;
987 | }
988 |
989 | .space-x-8 > :not([hidden]) ~ :not([hidden]) {
990 | --tw-space-x-reverse: 0;
991 | margin-right: calc(2rem * var(--tw-space-x-reverse));
992 | margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)));
993 | }
994 |
995 | .space-y-4 > :not([hidden]) ~ :not([hidden]) {
996 | --tw-space-y-reverse: 0;
997 | margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
998 | margin-bottom: calc(1rem * var(--tw-space-y-reverse));
999 | }
1000 |
1001 | .overflow-hidden {
1002 | overflow: hidden;
1003 | }
1004 |
1005 | .whitespace-nowrap {
1006 | white-space: nowrap;
1007 | }
1008 |
1009 | .rounded {
1010 | border-radius: 0.25rem;
1011 | }
1012 |
1013 | .rounded-lg {
1014 | border-radius: 0.5rem;
1015 | }
1016 |
1017 | .rounded-md {
1018 | border-radius: 0.375rem;
1019 | }
1020 |
1021 | .rounded-tl-md {
1022 | border-top-left-radius: 0.375rem;
1023 | }
1024 |
1025 | .rounded-tr-md {
1026 | border-top-right-radius: 0.375rem;
1027 | }
1028 |
1029 | .border {
1030 | border-width: 1px;
1031 | }
1032 |
1033 | .border-0 {
1034 | border-width: 0px;
1035 | }
1036 |
1037 | .border-b {
1038 | border-bottom-width: 1px;
1039 | }
1040 |
1041 | .border-b-2 {
1042 | border-bottom-width: 2px;
1043 | }
1044 |
1045 | .border-gray-200 {
1046 | --tw-border-opacity: 1;
1047 | border-color: rgb(229 231 235 / var(--tw-border-opacity));
1048 | }
1049 |
1050 | .border-gray-300 {
1051 | --tw-border-opacity: 1;
1052 | border-color: rgb(209 213 219 / var(--tw-border-opacity));
1053 | }
1054 |
1055 | .border-green-500 {
1056 | --tw-border-opacity: 1;
1057 | border-color: rgb(34 197 94 / var(--tw-border-opacity));
1058 | }
1059 |
1060 | .border-transparent {
1061 | border-color: transparent;
1062 | }
1063 |
1064 | .bg-green-50 {
1065 | --tw-bg-opacity: 1;
1066 | background-color: rgb(240 253 244 / var(--tw-bg-opacity));
1067 | }
1068 |
1069 | .bg-white {
1070 | --tw-bg-opacity: 1;
1071 | background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1072 | }
1073 |
1074 | .bg-opacity-75 {
1075 | --tw-bg-opacity: 0.75;
1076 | }
1077 |
1078 | .p-4 {
1079 | padding: 1rem;
1080 | }
1081 |
1082 | .px-1 {
1083 | padding-left: 0.25rem;
1084 | padding-right: 0.25rem;
1085 | }
1086 |
1087 | .px-2 {
1088 | padding-left: 0.5rem;
1089 | padding-right: 0.5rem;
1090 | }
1091 |
1092 | .px-3 {
1093 | padding-left: 0.75rem;
1094 | padding-right: 0.75rem;
1095 | }
1096 |
1097 | .px-4 {
1098 | padding-left: 1rem;
1099 | padding-right: 1rem;
1100 | }
1101 |
1102 | .py-1 {
1103 | padding-top: 0.25rem;
1104 | padding-bottom: 0.25rem;
1105 | }
1106 |
1107 | .py-1\.5 {
1108 | padding-top: 0.375rem;
1109 | padding-bottom: 0.375rem;
1110 | }
1111 |
1112 | .py-2 {
1113 | padding-top: 0.5rem;
1114 | padding-bottom: 0.5rem;
1115 | }
1116 |
1117 | .py-3\.5 {
1118 | padding-top: 0.875rem;
1119 | padding-bottom: 0.875rem;
1120 | }
1121 |
1122 | .py-4 {
1123 | padding-top: 1rem;
1124 | padding-bottom: 1rem;
1125 | }
1126 |
1127 | .py-6 {
1128 | padding-top: 1.5rem;
1129 | padding-bottom: 1.5rem;
1130 | }
1131 |
1132 | .pl-3 {
1133 | padding-left: 0.75rem;
1134 | }
1135 |
1136 | .pl-4 {
1137 | padding-left: 1rem;
1138 | }
1139 |
1140 | .pr-1\.5 {
1141 | padding-right: 0.375rem;
1142 | }
1143 |
1144 | .pr-10 {
1145 | padding-right: 2.5rem;
1146 | }
1147 |
1148 | .pr-14 {
1149 | padding-right: 3.5rem;
1150 | }
1151 |
1152 | .pr-3 {
1153 | padding-right: 0.75rem;
1154 | }
1155 |
1156 | .pr-4 {
1157 | padding-right: 1rem;
1158 | }
1159 |
1160 | .pt-0\.5 {
1161 | padding-top: 0.125rem;
1162 | }
1163 |
1164 | .text-left {
1165 | text-align: left;
1166 | }
1167 |
1168 | .text-center {
1169 | text-align: center;
1170 | }
1171 |
1172 | .text-right {
1173 | text-align: right;
1174 | }
1175 |
1176 | .align-middle {
1177 | vertical-align: middle;
1178 | }
1179 |
1180 | .font-sans {
1181 | font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
1182 | }
1183 |
1184 | .text-4xl {
1185 | font-size: 2.25rem;
1186 | line-height: 2.5rem;
1187 | }
1188 |
1189 | .text-base {
1190 | font-size: 1rem;
1191 | line-height: 1.5rem;
1192 | }
1193 |
1194 | .text-sm {
1195 | font-size: 0.875rem;
1196 | line-height: 1.25rem;
1197 | }
1198 |
1199 | .text-xs {
1200 | font-size: 0.75rem;
1201 | line-height: 1rem;
1202 | }
1203 |
1204 | .font-bold {
1205 | font-weight: 700;
1206 | }
1207 |
1208 | .font-medium {
1209 | font-weight: 500;
1210 | }
1211 |
1212 | .font-semibold {
1213 | font-weight: 600;
1214 | }
1215 |
1216 | .text-gray-400 {
1217 | --tw-text-opacity: 1;
1218 | color: rgb(156 163 175 / var(--tw-text-opacity));
1219 | }
1220 |
1221 | .text-gray-500 {
1222 | --tw-text-opacity: 1;
1223 | color: rgb(107 114 128 / var(--tw-text-opacity));
1224 | }
1225 |
1226 | .text-gray-900 {
1227 | --tw-text-opacity: 1;
1228 | color: rgb(17 24 39 / var(--tw-text-opacity));
1229 | }
1230 |
1231 | .text-green-400 {
1232 | --tw-text-opacity: 1;
1233 | color: rgb(74 222 128 / var(--tw-text-opacity));
1234 | }
1235 |
1236 | .text-green-600 {
1237 | --tw-text-opacity: 1;
1238 | color: rgb(22 163 74 / var(--tw-text-opacity));
1239 | }
1240 |
1241 | .text-green-700 {
1242 | --tw-text-opacity: 1;
1243 | color: rgb(21 128 61 / var(--tw-text-opacity));
1244 | }
1245 |
1246 | .shadow-lg {
1247 | --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
1248 | --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
1249 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1250 | }
1251 |
1252 | .shadow-sm {
1253 | --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
1254 | --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
1255 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
1256 | }
1257 |
1258 | .ring-1 {
1259 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1260 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1261 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1262 | }
1263 |
1264 | .ring-inset {
1265 | --tw-ring-inset: inset;
1266 | }
1267 |
1268 | .ring-black {
1269 | --tw-ring-opacity: 1;
1270 | --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity));
1271 | }
1272 |
1273 | .ring-gray-300 {
1274 | --tw-ring-opacity: 1;
1275 | --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity));
1276 | }
1277 |
1278 | .ring-green-600\/20 {
1279 | --tw-ring-color: rgb(22 163 74 / 0.2);
1280 | }
1281 |
1282 | .ring-opacity-5 {
1283 | --tw-ring-opacity: 0.05;
1284 | }
1285 |
1286 | .backdrop-blur {
1287 | --tw-backdrop-blur: blur(8px);
1288 | -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);
1289 | 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);
1290 | }
1291 |
1292 | .backdrop-filter {
1293 | -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);
1294 | 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);
1295 | }
1296 |
1297 | .placeholder\:text-gray-400::-moz-placeholder {
1298 | --tw-text-opacity: 1;
1299 | color: rgb(156 163 175 / var(--tw-text-opacity));
1300 | }
1301 |
1302 | .placeholder\:text-gray-400::placeholder {
1303 | --tw-text-opacity: 1;
1304 | color: rgb(156 163 175 / var(--tw-text-opacity));
1305 | }
1306 |
1307 | .hover\:border-gray-300:hover {
1308 | --tw-border-opacity: 1;
1309 | border-color: rgb(209 213 219 / var(--tw-border-opacity));
1310 | }
1311 |
1312 | .hover\:bg-green-50:hover {
1313 | --tw-bg-opacity: 1;
1314 | background-color: rgb(240 253 244 / var(--tw-bg-opacity));
1315 | }
1316 |
1317 | .hover\:text-gray-500:hover {
1318 | --tw-text-opacity: 1;
1319 | color: rgb(107 114 128 / var(--tw-text-opacity));
1320 | }
1321 |
1322 | .hover\:text-gray-700:hover {
1323 | --tw-text-opacity: 1;
1324 | color: rgb(55 65 81 / var(--tw-text-opacity));
1325 | }
1326 |
1327 | .focus\:border-green-500:focus {
1328 | --tw-border-opacity: 1;
1329 | border-color: rgb(34 197 94 / var(--tw-border-opacity));
1330 | }
1331 |
1332 | .focus\:outline-none:focus {
1333 | outline: 2px solid transparent;
1334 | outline-offset: 2px;
1335 | }
1336 |
1337 | .focus\:ring-2:focus {
1338 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1339 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1340 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1341 | }
1342 |
1343 | .focus\:ring-inset:focus {
1344 | --tw-ring-inset: inset;
1345 | }
1346 |
1347 | .focus\:ring-green-500:focus {
1348 | --tw-ring-opacity: 1;
1349 | --tw-ring-color: rgb(34 197 94 / var(--tw-ring-opacity));
1350 | }
1351 |
1352 | .focus\:ring-green-600:focus {
1353 | --tw-ring-opacity: 1;
1354 | --tw-ring-color: rgb(22 163 74 / var(--tw-ring-opacity));
1355 | }
1356 |
1357 | .focus\:ring-offset-2:focus {
1358 | --tw-ring-offset-width: 2px;
1359 | }
1360 |
1361 | .active\:ring-2:active {
1362 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1363 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1364 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1365 | }
1366 |
1367 | .active\:ring-green-600:active {
1368 | --tw-ring-opacity: 1;
1369 | --tw-ring-color: rgb(22 163 74 / var(--tw-ring-opacity));
1370 | }
1371 |
1372 | .active\:ring-offset-2:active {
1373 | --tw-ring-offset-width: 2px;
1374 | }
1375 |
1376 | .group:hover .group-hover\:visible {
1377 | visibility: visible;
1378 | }
1379 |
1380 | .peer\/search:checked ~ .peer-checked\/search\:border-green-500 {
1381 | --tw-border-opacity: 1;
1382 | border-color: rgb(34 197 94 / var(--tw-border-opacity));
1383 | }
1384 |
1385 | .peer\/util:checked ~ .peer-checked\/util\:border-green-500 {
1386 | --tw-border-opacity: 1;
1387 | border-color: rgb(34 197 94 / var(--tw-border-opacity));
1388 | }
1389 |
1390 | .peer\/search:checked ~ .peer-checked\/search\:text-green-600 {
1391 | --tw-text-opacity: 1;
1392 | color: rgb(22 163 74 / var(--tw-text-opacity));
1393 | }
1394 |
1395 | .peer\/util:checked ~ .peer-checked\/util\:text-green-600 {
1396 | --tw-text-opacity: 1;
1397 | color: rgb(22 163 74 / var(--tw-text-opacity));
1398 | }
1399 |
1400 | @media (min-width: 640px) {
1401 | .sm\:-mx-6 {
1402 | margin-left: -1.5rem;
1403 | margin-right: -1.5rem;
1404 | }
1405 |
1406 | .sm\:block {
1407 | display: block;
1408 | }
1409 |
1410 | .sm\:hidden {
1411 | display: none;
1412 | }
1413 |
1414 | .sm\:items-start {
1415 | align-items: flex-start;
1416 | }
1417 |
1418 | .sm\:items-end {
1419 | align-items: flex-end;
1420 | }
1421 |
1422 | .sm\:p-6 {
1423 | padding: 1.5rem;
1424 | }
1425 |
1426 | .sm\:px-6 {
1427 | padding-left: 1.5rem;
1428 | padding-right: 1.5rem;
1429 | }
1430 |
1431 | .sm\:pl-6 {
1432 | padding-left: 1.5rem;
1433 | }
1434 |
1435 | .sm\:pr-8 {
1436 | padding-right: 2rem;
1437 | }
1438 |
1439 | .sm\:text-sm {
1440 | font-size: 0.875rem;
1441 | line-height: 1.25rem;
1442 | }
1443 |
1444 | .sm\:text-sm\/6 {
1445 | font-size: 0.875rem;
1446 | line-height: 1.5rem;
1447 | }
1448 | }
1449 |
1450 | @media (min-width: 1024px) {
1451 | .lg\:-mx-8 {
1452 | margin-left: -2rem;
1453 | margin-right: -2rem;
1454 | }
1455 |
1456 | .lg\:px-8 {
1457 | padding-left: 2rem;
1458 | padding-right: 2rem;
1459 | }
1460 |
1461 | .lg\:pl-8 {
1462 | padding-left: 2rem;
1463 | }
1464 |
1465 | .lg\:pr-8 {
1466 | padding-right: 2rem;
1467 | }
1468 | }
1469 |
--------------------------------------------------------------------------------