diff --git a/cypress/e2e/training-datasets.cy.js b/cypress/e2e/training-datasets.cy.js index 6d4f863..a8a47ce 100644 --- a/cypress/e2e/training-datasets.cy.js +++ b/cypress/e2e/training-datasets.cy.js @@ -70,4 +70,37 @@ describe('Training Dataset Management', () => { expect($input[0].validationMessage).to.contain('Please fill out this field.'); }); }); + + it('validation error for duplicated name', () => { + cy.get('#training-datasets-listing div div a div').first().invoke('text').then(name => { + let alreadyExisting = name + cy.wrap(alreadyExisting).as('alreadyExisting') + }) + cy.get('main a') + .contains('Create Training Dataset') + .click(); + + cy.get('@alreadyExisting').then(alreadyExisting => { + cy.get('#train-dataset-name').type(alreadyExisting); + }) + + cy.get('#find-aods-form input').type('/alice/sim/2024/LHC24f3/0/523397'); + cy.get('#find-aods-form button').click(); + + cy.get('#file-list ul').children().its('length').should('be.gt', 0); + + selectFiles([ + '#file-list li:first-child', + '#file-list li:first-child', + '#file-list li:first-child', + '#file-list li:first-child', + '#file-list li:last-child' + ]); + + cy.get('#submit-dataset-form button[type="submit"]') + .contains('Submit') + .click(); + + cy.get('#errors').invoke('text').should('eq', 'Name must be unique\n') + }); }); diff --git a/cypress/e2e/training-machines.cy.js b/cypress/e2e/training-machines.cy.js index 60b3283..d5731f7 100644 --- a/cypress/e2e/training-machines.cy.js +++ b/cypress/e2e/training-machines.cy.js @@ -48,4 +48,22 @@ describe('Training Machine Management', () => { expect($input[0].validationMessage).to.contain('Please fill out this field.'); }); }); + + it('validation error for duplicated name', () => { + cy.get('tbody tr').first().children().first().invoke('text').then(name => { + let alreadyExisting = name + cy.wrap(alreadyExisting).as('alreadyExisting') + }) + + cy.get('main a') + .contains('Register Training Machine') + .click(); + + cy.get('@alreadyExisting').then(alreadyExisting => { + cy.get('input[name="name"]').type(alreadyExisting); + }) + cy.get('button').click(); + + cy.get('#errors').invoke('text').should('eq', 'Name must be unique\n') + }); }); \ No newline at end of file diff --git a/cypress/e2e/training-tasks.cy.js b/cypress/e2e/training-tasks.cy.js index 59e6479..e7baada 100644 --- a/cypress/e2e/training-tasks.cy.js +++ b/cypress/e2e/training-tasks.cy.js @@ -23,7 +23,7 @@ describe('Training Tasks Management', () => { cy.get('select[name="trainingDatasetId"]').select('LHC24b1b'); cy.get('button').click(); - let tmObject = cy.contains('tr', testName) + let tmObject = cy.contains('tr', testName) tmObject.should('exist') }); @@ -46,4 +46,23 @@ describe('Training Tasks Management', () => { expect($input[0].validationMessage).to.contain('Please fill out this field.'); }); }); + + it('validation error for duplicated name', () => { + cy.get('tbody tr').first().children().first().invoke('text').then(name => { + let alreadyExisting = name + cy.wrap(alreadyExisting).as('alreadyExisting') + }) + + cy.get('main a') + .contains('Create Training Task') + .click(); + + cy.get('@alreadyExisting').then(alreadyExisting => { + cy.get('input[name="name"]').type(alreadyExisting); + }) + cy.get('select[name="trainingDatasetId"]').select('LHC24b1b'); + cy.get('button').click(); + + cy.get('#errors').invoke('text').should('eq', 'Name must be unique\n') + }); }); \ No newline at end of file diff --git a/internal/handler/common.go b/internal/handler/common.go index 80b7880..038148b 100644 --- a/internal/handler/common.go +++ b/internal/handler/common.go @@ -6,6 +6,10 @@ import ( "github.com/mytkom/AliceTraINT/internal/service" ) +const ( + errMsgUserUnauthorized string = "user unauthorized" +) + func handleServiceError(w http.ResponseWriter, err error) { switch err.(type) { case *service.ErrHandlerNotFound: diff --git a/internal/handler/queue_handler.go b/internal/handler/queue_handler.go index ab111bb..59abe9a 100644 --- a/internal/handler/queue_handler.go +++ b/internal/handler/queue_handler.go @@ -2,6 +2,7 @@ package handler import ( "encoding/json" + "errors" "fmt" "net/http" "strconv" @@ -17,6 +18,10 @@ type QueueHandler struct { QueueService service.IQueueService } +const ( + errMsgUnauthorizedMachine string = "unauthorized machine" +) + func (qh *QueueHandler) parseId(r *http.Request) (uint, error) { idStr := r.PathValue("id") id, err := strconv.ParseUint(idStr, 10, 32) @@ -39,12 +44,12 @@ func (qh *QueueHandler) trainingMachineFromPath(r *http.Request) (*models.Traini } if tt.TrainingMachineId == nil { - return nil, nil, fmt.Errorf("unauthorized machine") + return nil, nil, errors.New(errMsgUnauthorizedMachine) } tm, err := qh.QueueService.AuthorizeTrainingMachine(r.Header.Get("Secret-Id"), *tt.TrainingMachineId) if err != nil { - return nil, nil, fmt.Errorf("unauthorized machine") + return nil, nil, errors.New(errMsgUnauthorizedMachine) } return tm, tt, nil @@ -57,7 +62,7 @@ func (qh *QueueHandler) UpdateStatus(w http.ResponseWriter, r *http.Request) { _, tt, err := qh.trainingMachineFromPath(r) if err != nil { - http.Error(w, "unauthorized machine", http.StatusUnauthorized) + http.Error(w, err.Error(), http.StatusUnauthorized) return } @@ -84,7 +89,7 @@ func (qh *QueueHandler) QueryTask(w http.ResponseWriter, r *http.Request) { tm, err := qh.QueueService.AuthorizeTrainingMachine(r.Header.Get("Secret-Id"), uint(tmId)) if err != nil { - http.Error(w, "unauthorized machine", http.StatusUnauthorized) + http.Error(w, errMsgUnauthorizedMachine, http.StatusUnauthorized) return } @@ -115,7 +120,7 @@ func (qh *QueueHandler) QueryTask(w http.ResponseWriter, r *http.Request) { func (qh *QueueHandler) CreateTrainingTaskResult(w http.ResponseWriter, r *http.Request) { _, tt, err := qh.trainingMachineFromPath(r) if err != nil { - http.Error(w, "unauthorized machine", http.StatusUnauthorized) + http.Error(w, err.Error(), http.StatusUnauthorized) return } diff --git a/internal/handler/training_dataset_handler.go b/internal/handler/training_dataset_handler.go index f63e9ad..d9347ad 100644 --- a/internal/handler/training_dataset_handler.go +++ b/internal/handler/training_dataset_handler.go @@ -48,7 +48,7 @@ func (h *TrainingDatasetHandler) List(w http.ResponseWriter, r *http.Request) { user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } @@ -119,7 +119,7 @@ func (h *TrainingDatasetHandler) Create(w http.ResponseWriter, r *http.Request) user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } trainingDataset.UserId = user.ID @@ -144,7 +144,7 @@ func (h *TrainingDatasetHandler) Delete(w http.ResponseWriter, r *http.Request) user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } diff --git a/internal/handler/training_machine_handler.go b/internal/handler/training_machine_handler.go index 530c4d3..3bc915c 100644 --- a/internal/handler/training_machine_handler.go +++ b/internal/handler/training_machine_handler.go @@ -47,13 +47,13 @@ func (h *TrainingMachineHandler) List(w http.ResponseWriter, r *http.Request) { user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } trainingMachines, err := h.Service.GetAll(user.ID, utils.IsUserScoped(r)) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + handleServiceError(w, err) return } @@ -80,7 +80,7 @@ func (h *TrainingMachineHandler) Show(w http.ResponseWriter, r *http.Request) { trainingMachine, err := h.Service.GetByID(uint(id)) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + handleServiceError(w, err) return } @@ -123,14 +123,14 @@ func (h *TrainingMachineHandler) Create(w http.ResponseWriter, r *http.Request) user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } trainingMachine.UserId = user.ID secretKey, err := h.Service.Create(&trainingMachine) if err != nil { - http.Error(w, err.Error(), http.StatusUnauthorized) + handleServiceError(w, err) return } @@ -154,13 +154,13 @@ func (h *TrainingMachineHandler) Delete(w http.ResponseWriter, r *http.Request) user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } err = h.Service.Delete(user.ID, uint(trainingMachineId)) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + handleServiceError(w, err) return } diff --git a/internal/handler/training_task_handler.go b/internal/handler/training_task_handler.go index 7d9c269..619947c 100644 --- a/internal/handler/training_task_handler.go +++ b/internal/handler/training_task_handler.go @@ -47,13 +47,14 @@ func (h *TrainingTaskHandler) List(w http.ResponseWriter, r *http.Request) { user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } trainingTasks, err := h.Service.GetAll(user.ID, utils.IsUserScoped(r)) if err != nil { handleServiceError(w, err) + return } err = h.ExecuteTemplate(w, "training-tasks_list", TemplateData{ @@ -61,6 +62,7 @@ func (h *TrainingTaskHandler) List(w http.ResponseWriter, r *http.Request) { }) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) + return } } @@ -124,7 +126,7 @@ func (h *TrainingTaskHandler) New(w http.ResponseWriter, r *http.Request) { user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } @@ -155,14 +157,14 @@ func (h *TrainingTaskHandler) Create(w http.ResponseWriter, r *http.Request) { user, ok := middleware.GetLoggedUser(r) if !ok || user == nil { - http.Error(w, "user not found in context", http.StatusUnauthorized) + http.Error(w, errMsgUserUnauthorized, http.StatusUnauthorized) return } trainingTask.UserId = user.ID err = h.Service.Create(&trainingTask) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + handleServiceError(w, err) return } diff --git a/internal/service/training_machine_service.go b/internal/service/training_machine_service.go index 41b7634..b67de4d 100644 --- a/internal/service/training_machine_service.go +++ b/internal/service/training_machine_service.go @@ -1,8 +1,11 @@ package service import ( + "errors" + "github.com/mytkom/AliceTraINT/internal/db/models" "github.com/mytkom/AliceTraINT/internal/db/repository" + "gorm.io/gorm" ) type ITrainingMachineService interface { @@ -24,6 +27,8 @@ func NewTrainingMachineService(repo *repository.RepositoryContext, hasher Hasher } } +var errMachineNotFound = NewErrHandlerNotFound("TrainingMachine") + func (s *TrainingMachineService) Create(tm *models.TrainingMachine) (string, error) { secretKey, err := s.Hasher.GenerateKey() if err != nil { @@ -37,7 +42,14 @@ func (s *TrainingMachineService) Create(tm *models.TrainingMachine) (string, err err = s.TrainingMachine.Create(tm) if err != nil { - return "", err + if errors.Is(err, gorm.ErrDuplicatedKey) { + return "", &ErrHandlerValidation{ + Field: "Name", + Msg: errMsgNotUnique, + } + } else { + return "", errInternalServerError + } } return secretKey, nil @@ -50,12 +62,12 @@ func (s *TrainingMachineService) GetAll(loggedUserId uint, userScoped bool) ([]m if userScoped { trainingMachines, err = s.TrainingMachine.GetAllUser(loggedUserId) if err != nil { - return nil, err + return nil, errInternalServerError } } else { trainingMachines, err = s.TrainingMachine.GetAll() if err != nil { - return nil, err + return nil, errInternalServerError } } @@ -63,9 +75,27 @@ func (s *TrainingMachineService) GetAll(loggedUserId uint, userScoped bool) ([]m } func (s *TrainingMachineService) GetByID(id uint) (*models.TrainingMachine, error) { - return s.TrainingMachine.GetByID(uint(id)) + tm, err := s.TrainingMachine.GetByID(uint(id)) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errMachineNotFound + } else { + return nil, errInternalServerError + } + } + + return tm, nil } func (s *TrainingMachineService) Delete(loggedUserId uint, id uint) error { - return s.TrainingMachine.Delete(loggedUserId, id) + err := s.TrainingMachine.Delete(loggedUserId, id) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return errMachineNotFound + } else { + return errInternalServerError + } + } + + return nil } diff --git a/internal/service/training_task_service.go b/internal/service/training_task_service.go index 959b4f7..d951d3a 100644 --- a/internal/service/training_task_service.go +++ b/internal/service/training_task_service.go @@ -8,6 +8,7 @@ import ( "github.com/mytkom/AliceTraINT/internal/ccdb" "github.com/mytkom/AliceTraINT/internal/db/models" "github.com/mytkom/AliceTraINT/internal/db/repository" + "gorm.io/gorm" ) type TrainingTaskWithResults struct { @@ -45,11 +46,25 @@ func NewTrainingTaskService(repo *repository.RepositoryContext, ccdbService ICCD } } +var errTaskNotFound = NewErrHandlerNotFound("TrainingTask") + func (s *TrainingTaskService) Create(tt *models.TrainingTask) error { // Status must start with Queued tt.Status = models.Queued - return s.TrainingTask.Create(tt) + err := s.TrainingTask.Create(tt) + if err != nil { + if errors.Is(err, gorm.ErrDuplicatedKey) { + return &ErrHandlerValidation{ + Field: "Name", + Msg: errMsgNotUnique, + } + } else { + return errInternalServerError + } + } + + return nil } func (s *TrainingTaskService) GetAll(loggedUserId uint, userScoped bool) ([]models.TrainingTask, error) { @@ -59,12 +74,12 @@ func (s *TrainingTaskService) GetAll(loggedUserId uint, userScoped bool) ([]mode if userScoped { trainingTasks, err = s.TrainingTask.GetAllUser(loggedUserId) if err != nil { - return nil, err + return nil, errInternalServerError } } else { trainingTasks, err = s.TrainingTask.GetAll() if err != nil { - return nil, err + return nil, errInternalServerError } } @@ -74,7 +89,7 @@ func (s *TrainingTaskService) GetAll(loggedUserId uint, userScoped bool) ([]mode func (s *TrainingTaskService) GetHelpers(loggedUserId uint) (*TrainingTaskHelpers, error) { trainingDatasets, err := s.TrainingDataset.GetAllUser(loggedUserId) if err != nil { - return nil, err + return nil, errInternalServerError } return &TrainingTaskHelpers{ @@ -86,14 +101,18 @@ func (s *TrainingTaskService) GetHelpers(loggedUserId uint) (*TrainingTaskHelper func (s *TrainingTaskService) GetByID(id uint) (*TrainingTaskWithResults, error) { trainingTask, err := s.TrainingTask.GetByID(uint(id)) if err != nil { - return nil, err + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errTaskNotFound + } else { + return nil, errInternalServerError + } } var imageFiles []models.TrainingTaskResult if trainingTask.Status >= models.Completed { imageFiles, err = s.TrainingTaskResult.GetByType(trainingTask.ID, models.Image) if err != nil { - return nil, err + return nil, errInternalServerError } } else { imageFiles = nil @@ -103,7 +122,7 @@ func (s *TrainingTaskService) GetByID(id uint) (*TrainingTaskWithResults, error) if trainingTask.Status >= models.Benchmarking { onnxFiles, err = s.TrainingTaskResult.GetByType(trainingTask.ID, models.Onnx) if err != nil { - return nil, err + return nil, errInternalServerError } } else { onnxFiles = nil @@ -119,7 +138,11 @@ func (s *TrainingTaskService) GetByID(id uint) (*TrainingTaskWithResults, error) func (s *TrainingTaskService) UploadOnnxResults(id uint) error { trainingTask, err := s.TrainingTask.GetByID(id) if err != nil { - return err + if errors.Is(err, gorm.ErrRecordNotFound) { + return errTaskNotFound + } else { + return errInternalServerError + } } if trainingTask.Status < models.Completed { @@ -152,7 +175,7 @@ func (s *TrainingTaskService) UploadOnnxResults(id uint) error { trainingTask.Status = models.Uploaded if err := s.TrainingTask.Update(trainingTask); err != nil { - return err + return errInternalServerError } return nil diff --git a/static/css/output.css b/static/css/output.css index cd4cd3e..2c8bc8d 100644 --- a/static/css/output.css +++ b/static/css/output.css @@ -1 +1,1617 @@ -/*! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%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 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:1rem;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:transparent}[type=checkbox]:indeterminate{background-color:currentColor;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:transparent}@media (forced-colors:active) {[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:transparent}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.left-4{left:1rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.z-10{z-index:10}.col-span-2{grid-column:span 2/span 2}.col-start-1{grid-column-start:1}.col-start-2{grid-column-start:2}.row-start-2{grid-row-start:2}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-bottom:1rem;margin-top:1rem}.mb-5{margin-bottom:1.25rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.block{display:block}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.size-14{height:3.5rem;width:3.5rem}.size-4{height:1rem;width:1rem}.h-12{height:3rem}.h-3{height:.75rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-full{height:100%}.max-h-96{max-height:24rem}.max-h-full{max-height:100%}.max-h-screen{max-height:100vh}.min-h-48{min-height:12rem}.min-h-96{min-height:24rem}.min-h-screen{min-height:100vh}.w-16{width:4rem}.w-3{width:.75rem}.w-40{width:10rem}.w-5{width:1.25rem}.w-8{width:2rem}.w-full{width:100%}.flex-shrink{flex-shrink:1}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.transform{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))}.cursor-pointer{cursor:pointer}.auto-cols-auto{grid-auto-columns:auto}.auto-rows-auto{grid-auto-rows:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.content-stretch{align-content:stretch}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-stretch{justify-content:stretch}.gap-0{gap:0}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.self-end{align-self:flex-end}.self-center{align-self:center}.self-stretch{align-self:stretch}.justify-self-end{justify-self:end}.justify-self-stretch{justify-self:stretch}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-dotted{border-style:dotted}.border-sky-200{--tw-border-opacity:1;border-color:rgb(186 230 253/var(--tw-border-opacity))}.border-sky-900{--tw-border-opacity:1;border-color:rgb(12 74 110/var(--tw-border-opacity))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity))}.bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.bg-sky-100{--tw-bg-opacity:1;background-color:rgb(224 242 254/var(--tw-bg-opacity))}.bg-sky-200{--tw-bg-opacity:1;background-color:rgb(186 230 253/var(--tw-bg-opacity))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity))}.bg-sky-800{--tw-bg-opacity:1;background-color:rgb(7 89 133/var(--tw-bg-opacity))}.bg-sky-900{--tw-bg-opacity:1;background-color:rgb(12 74 110/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-200{--tw-bg-opacity:1;background-color:rgb(254 240 138/var(--tw-bg-opacity))}.bg-yellow-600{--tw-bg-opacity:1;background-color:rgb(202 138 4/var(--tw-bg-opacity))}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.font-extrabold{font-weight:800}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-normal{line-height:1.5}.text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.text-sky-50{--tw-text-opacity:1;color:rgb(240 249 255/var(--tw-text-opacity))}.text-sky-900{--tw-text-opacity:1;color:rgb(12 74 110/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.opacity-50{opacity:.5}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.display-htmx-indicator{display:none}.htmx-request .display-htmx-indicator,.htmx-request.display-htmx-indicator{display:inline}.hover\:cursor-pointer:hover{cursor:pointer}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.hover\:bg-red-400:hover{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity))}.hover\:bg-sky-100:hover{--tw-bg-opacity:1;background-color:rgb(224 242 254/var(--tw-bg-opacity))}.hover\:bg-sky-200:hover{--tw-bg-opacity:1;background-color:rgb(186 230 253/var(--tw-bg-opacity))}.hover\:bg-sky-700:hover{--tw-bg-opacity:1;background-color:rgb(3 105 161/var(--tw-bg-opacity))}.hover\:bg-sky-800:hover{--tw-bg-opacity:1;background-color:rgb(7 89 133/var(--tw-bg-opacity))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.group:hover .group-hover\:border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.group:hover .group-hover\:text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}@media (min-width:768px){.md\:grid{display:grid}.md\:auto-rows-auto{grid-auto-rows:auto}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:justify-between{justify-content:space-between}.md\:justify-self-end{justify-self:end}.md\:text-lg{font-size:1.125rem;line-height:1.75rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1024px){.lg\:size-20{height:5rem;width:5rem}.lg\:size-5{height:1.25rem;width:1.25rem}.lg\:size-6{height:1.5rem;width:1.5rem}.lg\:w-1\/2{width:50%}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}}@media (min-width:1280px){.xl\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (prefers-color-scheme:dark){.dark\:border-sky-700{--tw-border-opacity:1;border-color:rgb(3 105 161/var(--tw-border-opacity))}.dark\:bg-sky-600{--tw-bg-opacity:1;background-color:rgb(2 132 199/var(--tw-bg-opacity))}.dark\:bg-sky-700{--tw-bg-opacity:1;background-color:rgb(3 105 161/var(--tw-bg-opacity))}.dark\:bg-sky-800{--tw-bg-opacity:1;background-color:rgb(7 89 133/var(--tw-bg-opacity))}.dark\:bg-sky-900{--tw-bg-opacity:1;background-color:rgb(12 74 110/var(--tw-bg-opacity))}.dark\:bg-sky-950{--tw-bg-opacity:1;background-color:rgb(8 47 73/var(--tw-bg-opacity))}.dark\:text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.dark\:text-sky-50{--tw-text-opacity:1;color:rgb(240 249 255/var(--tw-text-opacity))}.dark\:shadow-none{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.dark\:hover\:bg-sky-700:hover{--tw-bg-opacity:1;background-color:rgb(3 105 161/var(--tw-bg-opacity))}.dark\:hover\:bg-sky-800:hover{--tw-bg-opacity:1;background-color:rgb(7 89 133/var(--tw-bg-opacity))}} \ No newline at end of file +/* +! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +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) +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) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +[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 { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[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 { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +::-webkit-datetime-edit { + display: inline-flex; +} + +::-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 { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple],[size]:where(select:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + 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"); +} + +@media (forced-colors: active) { + [type='checkbox']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='radio']:checked { + 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"); +} + +@media (forced-colors: active) { + [type='radio']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + 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"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +@media (forced-colors: active) { + [type='checkbox']:indeterminate { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.sticky { + position: sticky; +} + +.inset-0 { + inset: 0px; +} + +.left-4 { + left: 1rem; +} + +.right-4 { + right: 1rem; +} + +.top-0 { + top: 0px; +} + +.top-1\/2 { + top: 50%; +} + +.z-10 { + z-index: 10; +} + +.col-span-2 { + grid-column: span 2 / span 2; +} + +.col-start-1 { + grid-column-start: 1; +} + +.col-start-2 { + grid-column-start: 2; +} + +.row-start-2 { + grid-row-start: 2; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + +.mb-5 { + margin-bottom: 1.25rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-5 { + margin-top: 1.25rem; +} + +.block { + display: block; +} + +.flex { + display: flex; +} + +.table { + display: table; +} + +.grid { + display: grid; +} + +.hidden { + display: none; +} + +.size-14 { + width: 3.5rem; + height: 3.5rem; +} + +.size-4 { + width: 1rem; + height: 1rem; +} + +.h-12 { + height: 3rem; +} + +.h-3 { + height: 0.75rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-8 { + height: 2rem; +} + +.h-full { + height: 100%; +} + +.max-h-96 { + max-height: 24rem; +} + +.max-h-full { + max-height: 100%; +} + +.max-h-screen { + max-height: 100vh; +} + +.min-h-48 { + min-height: 12rem; +} + +.min-h-96 { + min-height: 24rem; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-16 { + width: 4rem; +} + +.w-3 { + width: 0.75rem; +} + +.w-40 { + width: 10rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-8 { + width: 2rem; +} + +.w-full { + width: 100%; +} + +.flex-shrink { + flex-shrink: 1; +} + +.flex-grow { + flex-grow: 1; +} + +.border-collapse { + border-collapse: collapse; +} + +.-translate-y-1\/2 { + --tw-translate-y: -50%; + 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)); +} + +.transform { + 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)); +} + +.cursor-pointer { + cursor: pointer; +} + +.auto-cols-auto { + grid-auto-columns: auto; +} + +.auto-rows-auto { + grid-auto-rows: auto; +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.grid-cols-7 { + grid-template-columns: repeat(7, minmax(0, 1fr)); +} + +.grid-rows-2 { + grid-template-rows: repeat(2, minmax(0, 1fr)); +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.content-stretch { + align-content: stretch; +} + +.items-center { + align-items: center; +} + +.items-stretch { + align-items: stretch; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-stretch { + justify-content: stretch; +} + +.gap-0 { + gap: 0px; +} + +.gap-1 { + gap: 0.25rem; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-3 { + gap: 0.75rem; +} + +.gap-4 { + gap: 1rem; +} + +.gap-5 { + gap: 1.25rem; +} + +.self-end { + align-self: flex-end; +} + +.self-center { + align-self: center; +} + +.self-stretch { + align-self: stretch; +} + +.justify-self-end { + justify-self: end; +} + +.justify-self-stretch { + justify-self: stretch; +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-x-auto { + overflow-x: auto; +} + +.overflow-y-auto { + overflow-y: auto; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.border { + border-width: 1px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-b-2 { + border-bottom-width: 2px; +} + +.border-dotted { + border-style: dotted; +} + +.border-sky-200 { + --tw-border-opacity: 1; + border-color: rgb(186 230 253 / var(--tw-border-opacity)); +} + +.border-sky-900 { + --tw-border-opacity: 1; + border-color: rgb(12 74 110 / var(--tw-border-opacity)); +} + +.bg-emerald-600 { + --tw-bg-opacity: 1; + background-color: rgb(5 150 105 / var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)); +} + +.bg-gray-700 { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.bg-green-400 { + --tw-bg-opacity: 1; + background-color: rgb(74 222 128 / var(--tw-bg-opacity)); +} + +.bg-green-600 { + --tw-bg-opacity: 1; + background-color: rgb(22 163 74 / var(--tw-bg-opacity)); +} + +.bg-red-400 { + --tw-bg-opacity: 1; + background-color: rgb(248 113 113 / var(--tw-bg-opacity)); +} + +.bg-red-600 { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} + +.bg-sky-100 { + --tw-bg-opacity: 1; + background-color: rgb(224 242 254 / var(--tw-bg-opacity)); +} + +.bg-sky-200 { + --tw-bg-opacity: 1; + background-color: rgb(186 230 253 / var(--tw-bg-opacity)); +} + +.bg-sky-50 { + --tw-bg-opacity: 1; + background-color: rgb(240 249 255 / var(--tw-bg-opacity)); +} + +.bg-sky-800 { + --tw-bg-opacity: 1; + background-color: rgb(7 89 133 / var(--tw-bg-opacity)); +} + +.bg-sky-900 { + --tw-bg-opacity: 1; + background-color: rgb(12 74 110 / var(--tw-bg-opacity)); +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-yellow-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 240 138 / var(--tw-bg-opacity)); +} + +.bg-yellow-600 { + --tw-bg-opacity: 1; + background-color: rgb(202 138 4 / var(--tw-bg-opacity)); +} + +.object-contain { + -o-object-fit: contain; + object-fit: contain; +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.p-2 { + padding: 0.5rem; +} + +.p-3 { + padding: 0.75rem; +} + +.p-5 { + padding: 1.25rem; +} + +.p-6 { + padding: 1.5rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + +.text-left { + text-align: left; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +.font-sans { + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.font-bold { + font-weight: 700; +} + +.font-extrabold { + font-weight: 800; +} + +.font-medium { + font-weight: 500; +} + +.font-normal { + font-weight: 400; +} + +.font-semibold { + font-weight: 600; +} + +.uppercase { + text-transform: uppercase; +} + +.leading-normal { + line-height: 1.5; +} + +.text-gray-50 { + --tw-text-opacity: 1; + color: rgb(249 250 251 / var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); +} + +.text-red-700 { + --tw-text-opacity: 1; + color: rgb(185 28 28 / var(--tw-text-opacity)); +} + +.text-sky-50 { + --tw-text-opacity: 1; + color: rgb(240 249 255 / var(--tw-text-opacity)); +} + +.text-sky-900 { + --tw-text-opacity: 1; + color: rgb(12 74 110 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-red-600 { + --tw-text-opacity: 1; + color: rgb(220 38 38 / var(--tw-text-opacity)); +} + +.opacity-50 { + opacity: 0.5; +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.display-htmx-indicator { + display: none; +} + +.htmx-request .display-htmx-indicator { + display: inline; +} + +.htmx-request.display-htmx-indicator { + display: inline; +} + +.hover\:cursor-pointer:hover { + cursor: pointer; +} + +.hover\:bg-gray-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)); +} + +.hover\:bg-red-400:hover { + --tw-bg-opacity: 1; + background-color: rgb(248 113 113 / var(--tw-bg-opacity)); +} + +.hover\:bg-sky-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(224 242 254 / var(--tw-bg-opacity)); +} + +.hover\:bg-sky-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(186 230 253 / var(--tw-bg-opacity)); +} + +.hover\:bg-sky-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(3 105 161 / var(--tw-bg-opacity)); +} + +.hover\:bg-sky-800:hover { + --tw-bg-opacity: 1; + background-color: rgb(7 89 133 / var(--tw-bg-opacity)); +} + +.hover\:bg-white:hover { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.group:hover .group-hover\:border-blue-500 { + --tw-border-opacity: 1; + border-color: rgb(59 130 246 / var(--tw-border-opacity)); +} + +.group:hover .group-hover\:text-blue-500 { + --tw-text-opacity: 1; + color: rgb(59 130 246 / var(--tw-text-opacity)); +} + +@media (min-width: 768px) { + .md\:grid { + display: grid; + } + + .md\:auto-rows-auto { + grid-auto-rows: auto; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:justify-between { + justify-content: space-between; + } + + .md\:justify-self-end { + justify-self: end; + } + + .md\:text-lg { + font-size: 1.125rem; + line-height: 1.75rem; + } + + .md\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } +} + +@media (min-width: 1024px) { + .lg\:size-20 { + width: 5rem; + height: 5rem; + } + + .lg\:size-5 { + width: 1.25rem; + height: 1.25rem; + } + + .lg\:size-6 { + width: 1.5rem; + height: 1.5rem; + } + + .lg\:w-1\/2 { + width: 50%; + } + + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .lg\:flex-row { + flex-direction: row; + } +} + +@media (min-width: 1280px) { + .xl\:text-lg { + font-size: 1.125rem; + line-height: 1.75rem; + } +} + +@media (prefers-color-scheme: dark) { + .dark\:border-sky-700 { + --tw-border-opacity: 1; + border-color: rgb(3 105 161 / var(--tw-border-opacity)); + } + + .dark\:bg-sky-600 { + --tw-bg-opacity: 1; + background-color: rgb(2 132 199 / var(--tw-bg-opacity)); + } + + .dark\:bg-sky-700 { + --tw-bg-opacity: 1; + background-color: rgb(3 105 161 / var(--tw-bg-opacity)); + } + + .dark\:bg-sky-800 { + --tw-bg-opacity: 1; + background-color: rgb(7 89 133 / var(--tw-bg-opacity)); + } + + .dark\:bg-sky-900 { + --tw-bg-opacity: 1; + background-color: rgb(12 74 110 / var(--tw-bg-opacity)); + } + + .dark\:bg-sky-950 { + --tw-bg-opacity: 1; + background-color: rgb(8 47 73 / var(--tw-bg-opacity)); + } + + .dark\:text-gray-50 { + --tw-text-opacity: 1; + color: rgb(249 250 251 / var(--tw-text-opacity)); + } + + .dark\:text-sky-50 { + --tw-text-opacity: 1; + color: rgb(240 249 255 / var(--tw-text-opacity)); + } + + .dark\:shadow-none { + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + } + + .dark\:hover\:bg-sky-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(3 105 161 / var(--tw-bg-opacity)); + } + + .dark\:hover\:bg-sky-800:hover { + --tw-bg-opacity: 1; + background-color: rgb(7 89 133 / var(--tw-bg-opacity)); + } +} \ No newline at end of file diff --git a/web/templates/static/head.html b/web/templates/static/head.html index 2dc9a00..68d275e 100644 --- a/web/templates/static/head.html +++ b/web/templates/static/head.html @@ -12,7 +12,8 @@ {{.Title}} - AliceTraINT - + + {{ end }} \ No newline at end of file diff --git a/web/templates/training-datasets/new.html b/web/templates/training-datasets/new.html index 4868d75..7318969 100644 --- a/web/templates/training-datasets/new.html +++ b/web/templates/training-datasets/new.html @@ -37,9 +37,16 @@ -
+

Create training Dataset

-
+ +
diff --git a/web/templates/training-machines/list.html b/web/templates/training-machines/list.html index f8a4308..3b62bba 100644 --- a/web/templates/training-machines/list.html +++ b/web/templates/training-machines/list.html @@ -12,14 +12,21 @@ {{ range .TrainingMachines }} - + {{ .Name }} {{ .User.FirstName }} {{ .User.FamilyName}} + {{ if .LastActivityAt.IsZero }} + Never Active + {{ else }} {{ .LastActivityAt.Format "02 Jan 06 15:04 MST" }} + {{ end }} {{ .CreatedAt.Format "02 Jan 06 15:04 MST" }} {{ .UpdatedAt.Format "02 Jan 06 15:04 MST" }} - + {{ end }} diff --git a/web/templates/training-machines/new.html b/web/templates/training-machines/new.html index 8700621..eb0fde3 100644 --- a/web/templates/training-machines/new.html +++ b/web/templates/training-machines/new.html @@ -1,22 +1,29 @@ {{ define "training-machines_new" }} {{ template "header" . }} - -

Register Training Machine

-
-

Name:

-
- -
- -
-
- +
+ +

Register Training Machine

+
+
+

Name:

+
+ +
+ +
+
+ +
-
- + +
{{ template "footer" . }} {{ end }} \ No newline at end of file diff --git a/web/templates/training-tasks/new.html b/web/templates/training-tasks/new.html index df582d9..26b3f0b 100644 --- a/web/templates/training-tasks/new.html +++ b/web/templates/training-tasks/new.html @@ -1,60 +1,70 @@ {{ define "training-tasks_new" }} {{ template "header" . }} -
-

Create Training Task

-
-

Basic configuration:

-
-
- -
-
- -
-
- -
-
- -
-
- -

Additional configuration:

-
- {{range $field, $spec := .FieldConfigs}} -
-
- - - {{if eq $spec.Type "uint" "int"}} - - {{else if eq $spec.Type "float64"}} - - {{else if eq $spec.Type "bool"}} - - {{else}} - - {{end}} +
+ +

Create Training Task

+
+
+

Basic configuration:

+
+
+
-
-

{{$spec.Description}}

+
+ +
+
+ +
+
+
- {{end}} -
- -
+

Additional configuration:

+
+ {{range $field, $spec := .FieldConfigs}} +
+
+ - + {{if eq $spec.Type "uint" "int"}} + + {{else if eq $spec.Type "float64"}} + + {{else if eq $spec.Type "bool"}} + + {{else}} + + {{end}} +
+
+

{{$spec.Description}}

+
+
+ {{end}} +
+ +
+ +
{{ template "footer" . }} {{ end }} \ No newline at end of file