feat: contact form and more edits
@@ -1,6 +1,6 @@
|
|||||||
# Tecvico Website
|
# Radiuma Website
|
||||||
|
|
||||||
Django MVT informational website for **Tecvico Corp** (formerly Visera), showcasing the **ViSERA** medical imaging and radiomics software suite.
|
Django MVT informational website for **Radiuma**, showcasing the **Radiuma** medical imaging and radiomics software suite.
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ tecvico_website/
|
|||||||
| URL | View | Description |
|
| URL | View | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `/` | `HomeView` | Landing page |
|
| `/` | `HomeView` | Landing page |
|
||||||
| `/about/` | `AboutView` | What is Tecvico |
|
| `/about/` | `AboutView` | What is Radiuma |
|
||||||
| `/products/` | `ProductOverviewView` | All main products |
|
| `/products/` | `ProductOverviewView` | All main products |
|
||||||
| `/products/<main-slug>/` | `MainProductDetailView` | Main product + sub-products |
|
| `/products/<main-slug>/` | `MainProductDetailView` | Main product + sub-products |
|
||||||
| `/products/<main-slug>/<sub-slug>/` | `SubProductDetailView` | Sub-product + articles |
|
| `/products/<main-slug>/<sub-slug>/` | `SubProductDetailView` | Sub-product + articles |
|
||||||
@@ -248,7 +248,7 @@ Access Django Admin at `/admin/` with superuser credentials.
|
|||||||
|
|
||||||
The `seed_content` command populates:
|
The `seed_content` command populates:
|
||||||
|
|
||||||
- **ViSERA** (MainProduct) with 5 sub-products:
|
- **Radiuma** (MainProduct) with 5 sub-products:
|
||||||
- Image Processing
|
- Image Processing
|
||||||
- Radiomics Features
|
- Radiomics Features
|
||||||
- Medical Image Visualization
|
- Medical Image Visualization
|
||||||
@@ -275,4 +275,4 @@ Key CSS custom properties are in `static/css/main.css` under `:root`.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Content is adapted from visera.ca under CC BY-NC-SA. Software code is proprietary to Tecvico Corp.
|
Content is adapted from visera.ca under CC BY-NC-SA. Software code is proprietary to Radiuma.
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ from apps.products.models import Article, ArticleSection, MainProduct, SubProduc
|
|||||||
|
|
||||||
MAIN_PRODUCTS = [
|
MAIN_PRODUCTS = [
|
||||||
{
|
{
|
||||||
"name": "ViSERA",
|
"name": "Radiuma",
|
||||||
"slug": "visera",
|
"slug": "radiuma",
|
||||||
"short_description": "Visualized & Standardized Environment for Radiomics Analysis",
|
"short_description": "Visualized & Standardized Environment for Radiomics Analysis",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA is a free, open-source software specialized for visualization, "
|
"Radiuma is a free, open-source software specialized for visualization, "
|
||||||
"processing, segmentation, registration, fusion and analysis of medical and "
|
"processing, segmentation, registration, fusion and analysis of medical and "
|
||||||
"biomedical images, including radiomics and machine learning analysis. "
|
"biomedical images, including radiomics and machine learning analysis. "
|
||||||
"ViSERA is a major, entirely-revamped upgrade to the original SERA "
|
"Radiuma is a major, entirely-revamped upgrade to the original SERA "
|
||||||
"(Matlab-based), now built on Python for broader accessibility and community "
|
"(Matlab-based), now built on Python for broader accessibility and community "
|
||||||
"contribution. It enables standardized and reproducible radiomic feature "
|
"contribution. It enables standardized and reproducible radiomic feature "
|
||||||
"extraction in compliance with the Image Biomarker Standardization Initiative "
|
"extraction in compliance with the Image Biomarker Standardization Initiative "
|
||||||
@@ -28,7 +28,7 @@ MAIN_PRODUCTS = [
|
|||||||
"description": (
|
"description": (
|
||||||
"Advanced image processing capabilities including standardized filtering "
|
"Advanced image processing capabilities including standardized filtering "
|
||||||
"techniques compliant with IBSI 2.0, image registration, fusion, and "
|
"techniques compliant with IBSI 2.0, image registration, fusion, and "
|
||||||
"Standardized Uptake Value (SUV) conversion. ViSERA employs popular "
|
"Standardized Uptake Value (SUV) conversion. Radiuma employs popular "
|
||||||
"image processing algorithms to create end-to-end standardized workflows "
|
"image processing algorithms to create end-to-end standardized workflows "
|
||||||
"for consistent, reproducible research outcomes."
|
"for consistent, reproducible research outcomes."
|
||||||
),
|
),
|
||||||
@@ -37,7 +37,7 @@ MAIN_PRODUCTS = [
|
|||||||
{
|
{
|
||||||
"title": "Image Filtering Techniques",
|
"title": "Image Filtering Techniques",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA implements a comprehensive set of image filtering techniques "
|
"Radiuma implements a comprehensive set of image filtering techniques "
|
||||||
"fully standardized against the Image Biomarker Standardization "
|
"fully standardized against the Image Biomarker Standardization "
|
||||||
"Initiative (IBSI) phase 2. These filters enable reproducible "
|
"Initiative (IBSI) phase 2. These filters enable reproducible "
|
||||||
"preprocessing across institutions and studies."
|
"preprocessing across institutions and studies."
|
||||||
@@ -50,13 +50,13 @@ MAIN_PRODUCTS = [
|
|||||||
"value": "Mean, Gaussian, Laplacian of Gaussian (LoG), Laws kernels, Gabor, Wavelets (PyWavelets), Log-Sigma",
|
"value": "Mean, Gaussian, Laplacian of Gaussian (LoG), Laws kernels, Gabor, Wavelets (PyWavelets), Log-Sigma",
|
||||||
"order": 2,
|
"order": 2,
|
||||||
},
|
},
|
||||||
{"title": "Author", "value": "Tecvico Corp R&D Team", "order": 3},
|
{"title": "Author", "value": "Radiuma R&D Team", "order": 3},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Image Registration & Fusion",
|
"title": "Image Registration & Fusion",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA provides robust image registration and fusion methods, "
|
"Radiuma provides robust image registration and fusion methods, "
|
||||||
"enabling multi-modal image alignment for PET/CT, PET/MRI, and "
|
"enabling multi-modal image alignment for PET/CT, PET/MRI, and "
|
||||||
"other combined modality studies. Standardized Uptake Value (SUV) "
|
"other combined modality studies. Standardized Uptake Value (SUV) "
|
||||||
"conversion is also supported."
|
"conversion is also supported."
|
||||||
@@ -75,7 +75,7 @@ MAIN_PRODUCTS = [
|
|||||||
"slug": "radiomics-features",
|
"slug": "radiomics-features",
|
||||||
"short_description": "IBSI 1.0 compliant handcrafted radiomic feature extraction",
|
"short_description": "IBSI 1.0 compliant handcrafted radiomic feature extraction",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA provides comprehensive handcrafted radiomic feature extraction "
|
"Radiuma provides comprehensive handcrafted radiomic feature extraction "
|
||||||
"fully standardized by the Image Biomarker Standardization Initiative "
|
"fully standardized by the Image Biomarker Standardization Initiative "
|
||||||
"(IBSI 1.0). Features are computed from segmented regions of interest "
|
"(IBSI 1.0). Features are computed from segmented regions of interest "
|
||||||
"across multiple image modalities, enabling reproducible quantitative "
|
"across multiple image modalities, enabling reproducible quantitative "
|
||||||
@@ -86,7 +86,7 @@ MAIN_PRODUCTS = [
|
|||||||
{
|
{
|
||||||
"title": "IBSI Compliant Feature Extraction",
|
"title": "IBSI Compliant Feature Extraction",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA computes a comprehensive set of radiomic features "
|
"Radiuma computes a comprehensive set of radiomic features "
|
||||||
"covering all IBSI 1.0 feature classes. Features are extracted "
|
"covering all IBSI 1.0 feature classes. Features are extracted "
|
||||||
"from segmented Regions of Interest (ROIs) and are fully "
|
"from segmented Regions of Interest (ROIs) and are fully "
|
||||||
"reproducible across different platforms and institutions."
|
"reproducible across different platforms and institutions."
|
||||||
@@ -110,7 +110,7 @@ MAIN_PRODUCTS = [
|
|||||||
"slug": "medical-image-visualization",
|
"slug": "medical-image-visualization",
|
||||||
"short_description": "Professional multi-modality medical image viewer",
|
"short_description": "Professional multi-modality medical image viewer",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA includes a professional medical image viewer that supports "
|
"Radiuma includes a professional medical image viewer that supports "
|
||||||
"multiple imaging modalities and file formats. The viewer provides "
|
"multiple imaging modalities and file formats. The viewer provides "
|
||||||
"comfortable, intuitive controls for slice navigation, windowing, "
|
"comfortable, intuitive controls for slice navigation, windowing, "
|
||||||
"zoom, and annotation, suitable for radiation oncologists, radiologists, "
|
"zoom, and annotation, suitable for radiation oncologists, radiologists, "
|
||||||
@@ -140,7 +140,7 @@ MAIN_PRODUCTS = [
|
|||||||
"slug": "format-conversion",
|
"slug": "format-conversion",
|
||||||
"short_description": "Professional converter for medical imaging file formats",
|
"short_description": "Professional converter for medical imaging file formats",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA provides a professional image format converter supporting all "
|
"Radiuma provides a professional image format converter supporting all "
|
||||||
"major medical imaging standards. Seamlessly convert between DICOM, "
|
"major medical imaging standards. Seamlessly convert between DICOM, "
|
||||||
"NIFTI, NRRD, MHA, and other formats without loss of spatial metadata "
|
"NIFTI, NRRD, MHA, and other formats without loss of spatial metadata "
|
||||||
"or patient information integrity."
|
"or patient information integrity."
|
||||||
@@ -169,7 +169,7 @@ MAIN_PRODUCTS = [
|
|||||||
"slug": "workflow-management",
|
"slug": "workflow-management",
|
||||||
"short_description": "Reproducible research workflow creation and sharing",
|
"short_description": "Reproducible research workflow creation and sharing",
|
||||||
"description": (
|
"description": (
|
||||||
"ViSERA's workflow management system allows researchers to design, save, "
|
"Radiuma's workflow management system allows researchers to design, save, "
|
||||||
"share, and reuse analysis pipelines. Workflows connect individual "
|
"share, and reuse analysis pipelines. Workflows connect individual "
|
||||||
"processing steps — from image loading and preprocessing to feature "
|
"processing steps — from image loading and preprocessing to feature "
|
||||||
"extraction and machine learning — into reproducible, shareable sequences "
|
"extraction and machine learning — into reproducible, shareable sequences "
|
||||||
@@ -199,9 +199,9 @@ MAIN_PRODUCTS = [
|
|||||||
|
|
||||||
FAQ_ENTRIES = [
|
FAQ_ENTRIES = [
|
||||||
{
|
{
|
||||||
"question": "What is the ViSERA license?",
|
"question": "What is the Radiuma license?",
|
||||||
"answer": (
|
"answer": (
|
||||||
"ViSERA is free and open-source for research purposes.\n\n"
|
"Radiuma is free and open-source for research purposes.\n\n"
|
||||||
"License: CC BY-NC-SA (Creative Commons Attribution-NonCommercial-ShareAlike). "
|
"License: CC BY-NC-SA (Creative Commons Attribution-NonCommercial-ShareAlike). "
|
||||||
"This means you may use, share, and adapt the software for non-commercial "
|
"This means you may use, share, and adapt the software for non-commercial "
|
||||||
"research purposes, provided you give appropriate credit and distribute "
|
"research purposes, provided you give appropriate credit and distribute "
|
||||||
@@ -210,21 +210,21 @@ FAQ_ENTRIES = [
|
|||||||
"order": 1,
|
"order": 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"question": "How do I cite ViSERA in my research?",
|
"question": "How do I cite Radiuma in my research?",
|
||||||
"answer": (
|
"answer": (
|
||||||
"Please cite the following reference if you publish results obtained with "
|
"Please cite the following reference if you publish results obtained with "
|
||||||
"the help of ViSERA:\n\n"
|
"the help of Radiuma:\n\n"
|
||||||
"M. R. Salmanpour, I. Shiri, M. Hosseinzadeh, H. Zaidi, S. Ashrafinia, "
|
"M. R. Salmanpour, I. Shiri, M. Hosseinzadeh, H. Zaidi, S. Ashrafinia, "
|
||||||
"M. Oveisi, A. Rahmim. ViSERA: Visualized & Standardized Environment for "
|
"M. Oveisi, A. Rahmim. Radiuma: Visualized & Standardized Environment for "
|
||||||
"Radiomics Analysis — A Shareable, Executable, and Reproducible Workflow "
|
"Radiomics Analysis — A Shareable, Executable, and Reproducible Workflow "
|
||||||
"Generator. Proc. IEEE Medical Imaging Conference, 2023."
|
"Generator. Proc. IEEE Medical Imaging Conference, 2023."
|
||||||
),
|
),
|
||||||
"order": 2,
|
"order": 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"question": "Which operating systems does ViSERA support?",
|
"question": "Which operating systems does Radiuma support?",
|
||||||
"answer": (
|
"answer": (
|
||||||
"ViSERA currently fully supports Windows 10 and above (64-bit). "
|
"Radiuma currently fully supports Windows 10 and above (64-bit). "
|
||||||
"New versions to support macOS and Linux systems are under active development "
|
"New versions to support macOS and Linux systems are under active development "
|
||||||
"and coming soon. Follow our Discord or check the Downloads page for updates."
|
"and coming soon. Follow our Discord or check the Downloads page for updates."
|
||||||
),
|
),
|
||||||
@@ -241,9 +241,9 @@ FAQ_ENTRIES = [
|
|||||||
"order": 4,
|
"order": 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"question": "Is ViSERA suitable for clinical use?",
|
"question": "Is Radiuma suitable for clinical use?",
|
||||||
"answer": (
|
"answer": (
|
||||||
"ViSERA is designed and intended exclusively for research purposes. "
|
"Radiuma is designed and intended exclusively for research purposes. "
|
||||||
"It is not certified for clinical diagnostic use. Always consult with "
|
"It is not certified for clinical diagnostic use. Always consult with "
|
||||||
"qualified medical professionals for clinical decisions."
|
"qualified medical professionals for clinical decisions."
|
||||||
),
|
),
|
||||||
@@ -252,7 +252,7 @@ FAQ_ENTRIES = [
|
|||||||
{
|
{
|
||||||
"question": "Where can I get support or report issues?",
|
"question": "Where can I get support or report issues?",
|
||||||
"answer": (
|
"answer": (
|
||||||
"Support is available via email at support@tecvico.com and through our "
|
"Support is available via email at support@visera.ca and through our "
|
||||||
"community Discord server. For bug reports and feature requests, please "
|
"community Discord server. For bug reports and feature requests, please "
|
||||||
"use the Discord forum or contact us directly by email."
|
"use the Discord forum or contact us directly by email."
|
||||||
),
|
),
|
||||||
@@ -262,16 +262,16 @@ FAQ_ENTRIES = [
|
|||||||
|
|
||||||
DOWNLOAD_ITEMS = [
|
DOWNLOAD_ITEMS = [
|
||||||
{
|
{
|
||||||
"name": "ViSERA Desktop",
|
"name": "Radiuma Desktop",
|
||||||
"platform": "windows",
|
"platform": "windows",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"download_url": "https://github.com/tecvico/visera/releases/latest/download/ViSERA-Setup.exe",
|
"download_url": "https://github.com/radiuma/radiuma/releases/latest/download/Radiuma-Setup.exe",
|
||||||
"description": "Windows 10 and above (64-bit). Installer package.",
|
"description": "Windows 10 and above (64-bit). Installer package.",
|
||||||
"is_active": True,
|
"is_active": True,
|
||||||
"order": 1,
|
"order": 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ViSERA Desktop",
|
"name": "Radiuma Desktop",
|
||||||
"platform": "macos",
|
"platform": "macos",
|
||||||
"version": "Coming Soon",
|
"version": "Coming Soon",
|
||||||
"download_url": "#",
|
"download_url": "#",
|
||||||
@@ -280,7 +280,7 @@ DOWNLOAD_ITEMS = [
|
|||||||
"order": 2,
|
"order": 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ViSERA Desktop",
|
"name": "Radiuma Desktop",
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"version": "Coming Soon",
|
"version": "Coming Soon",
|
||||||
"download_url": "#",
|
"download_url": "#",
|
||||||
@@ -292,7 +292,7 @@ DOWNLOAD_ITEMS = [
|
|||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Seed the database with initial Tecvico / ViSERA content from visera.ca"
|
help = "Seed the database with initial Radiuma / Radiuma content from visera.ca"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import DownloadItem, FAQEntry
|
from .models import ContactSubmission, DownloadItem, FAQEntry
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ContactSubmission)
|
||||||
|
class ContactSubmissionAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("name", "title", "email", "submitted_at", "is_read")
|
||||||
|
list_filter = ("is_read", "submitted_at")
|
||||||
|
search_fields = ("name", "title", "description", "email")
|
||||||
|
list_editable = ("is_read",)
|
||||||
|
readonly_fields = ("name", "title", "description", "email", "submitted_at")
|
||||||
|
fieldsets = (
|
||||||
|
(None, {"fields": ("name", "title", "email", "description")}),
|
||||||
|
("Meta", {"fields": ("submitted_at", "is_read")}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(FAQEntry)
|
@admin.register(FAQEntry)
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
from .models import ContactSubmission
|
||||||
|
|
||||||
|
|
||||||
|
class ContactForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = ContactSubmission
|
||||||
|
fields = ["name", "title", "description", "email"]
|
||||||
|
widgets = {
|
||||||
|
"name": forms.TextInput(
|
||||||
|
attrs={"placeholder": "Your full name", "autocomplete": "name"}
|
||||||
|
),
|
||||||
|
"title": forms.TextInput(attrs={"placeholder": "Subject / topic"}),
|
||||||
|
"description": forms.Textarea(
|
||||||
|
attrs={"placeholder": "Write your message here…", "rows": 5}
|
||||||
|
),
|
||||||
|
"email": forms.EmailInput(
|
||||||
|
attrs={
|
||||||
|
"placeholder": "your@email.com (optional)",
|
||||||
|
"autocomplete": "email",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2026-05-04 09:13
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pages', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ContactSubmission',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=200)),
|
||||||
|
('title', models.CharField(max_length=300)),
|
||||||
|
('description', models.TextField()),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254)),
|
||||||
|
('submitted_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('is_read', models.BooleanField(default=False)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Contact Submission',
|
||||||
|
'verbose_name_plural': 'Contact Submissions',
|
||||||
|
'ordering': ['-submitted_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,6 +1,23 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class ContactSubmission(models.Model):
|
||||||
|
name = models.CharField(max_length=200)
|
||||||
|
title = models.CharField(max_length=300)
|
||||||
|
description = models.TextField()
|
||||||
|
email = models.EmailField(blank=True)
|
||||||
|
submitted_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
is_read = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["-submitted_at"]
|
||||||
|
verbose_name = "Contact Submission"
|
||||||
|
verbose_name_plural = "Contact Submissions"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} — {self.title}"
|
||||||
|
|
||||||
|
|
||||||
class FAQEntry(models.Model):
|
class FAQEntry(models.Model):
|
||||||
question = models.CharField(max_length=500)
|
question = models.CharField(max_length=500)
|
||||||
answer = models.TextField()
|
answer = models.TextField()
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ class AboutViewTest(TestCase):
|
|||||||
class DownloadsViewTest(TestCase):
|
class DownloadsViewTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
DownloadItem.objects.create(
|
DownloadItem.objects.create(
|
||||||
name="ViSERA Desktop",
|
name="Radiuma Desktop",
|
||||||
platform="windows",
|
platform="windows",
|
||||||
version="1.0",
|
version="1.0",
|
||||||
download_url="https://example.com/windows",
|
download_url="https://example.com/windows",
|
||||||
is_active=True,
|
is_active=True,
|
||||||
)
|
)
|
||||||
DownloadItem.objects.create(
|
DownloadItem.objects.create(
|
||||||
name="ViSERA Desktop",
|
name="Radiuma Desktop",
|
||||||
platform="macos",
|
platform="macos",
|
||||||
version="Coming Soon",
|
version="Coming Soon",
|
||||||
download_url="#",
|
download_url="#",
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.views import View
|
||||||
from django.views.generic import ListView, TemplateView
|
from django.views.generic import ListView, TemplateView
|
||||||
|
|
||||||
from .models import DownloadItem, FAQEntry
|
from .forms import ContactForm
|
||||||
|
from .models import ContactSubmission, DownloadItem, FAQEntry
|
||||||
|
|
||||||
|
|
||||||
class HomeView(TemplateView):
|
class HomeView(TemplateView):
|
||||||
@@ -36,5 +42,43 @@ class FAQView(ListView):
|
|||||||
queryset = FAQEntry.objects.filter(is_active=True)
|
queryset = FAQEntry.objects.filter(is_active=True)
|
||||||
|
|
||||||
|
|
||||||
class ContactView(TemplateView):
|
class ContactView(View):
|
||||||
template_name = "pages/contact.html"
|
template_name = "pages/contact.html"
|
||||||
|
|
||||||
|
def _new_captcha(self, request):
|
||||||
|
a, b = random.randint(1, 9), random.randint(1, 9)
|
||||||
|
request.session["captcha_answer"] = a + b
|
||||||
|
return f"{a} + {b}"
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
self.template_name,
|
||||||
|
{"form": ContactForm(), "captcha_question": self._new_captcha(request)},
|
||||||
|
)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
form = ContactForm(request.POST)
|
||||||
|
expected = request.session.get("captcha_answer")
|
||||||
|
captcha_question = self._new_captcha(request)
|
||||||
|
|
||||||
|
captcha_ok = False
|
||||||
|
try:
|
||||||
|
captcha_ok = int(request.POST.get("captcha_answer", "")) == expected
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if form.is_valid() and captcha_ok:
|
||||||
|
form.save()
|
||||||
|
return JsonResponse({"success": True})
|
||||||
|
|
||||||
|
errors: dict = {}
|
||||||
|
if not captcha_ok:
|
||||||
|
errors["captcha"] = ["Incorrect answer — please try again."]
|
||||||
|
errors.update(
|
||||||
|
{field: [str(e) for e in errs] for field, errs in form.errors.items()}
|
||||||
|
)
|
||||||
|
return JsonResponse(
|
||||||
|
{"success": False, "errors": errors, "captcha_question": captcha_question},
|
||||||
|
status=400,
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ from apps.products.models import Article, ArticleSection, MainProduct, SubProduc
|
|||||||
class ProductViewsSetup(TestCase):
|
class ProductViewsSetup(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.main_product = MainProduct.objects.create(
|
self.main_product = MainProduct.objects.create(
|
||||||
name="ViSERA",
|
name="Radiuma",
|
||||||
slug="visera",
|
slug="radiuma",
|
||||||
short_description="Radiomics software",
|
short_description="Radiomics software",
|
||||||
description="Full description.",
|
description="Full description.",
|
||||||
)
|
)
|
||||||
@@ -57,17 +57,17 @@ class ProductOverviewViewTest(ProductViewsSetup):
|
|||||||
|
|
||||||
class MainProductDetailViewTest(ProductViewsSetup):
|
class MainProductDetailViewTest(ProductViewsSetup):
|
||||||
def test_detail_returns_200(self):
|
def test_detail_returns_200(self):
|
||||||
url = reverse("products:main_product_detail", kwargs={"main_slug": "visera"})
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "radiuma"})
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_detail_uses_correct_template(self):
|
def test_detail_uses_correct_template(self):
|
||||||
url = reverse("products:main_product_detail", kwargs={"main_slug": "visera"})
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "radiuma"})
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertTemplateUsed(response, "products/main_detail.html")
|
self.assertTemplateUsed(response, "products/main_detail.html")
|
||||||
|
|
||||||
def test_detail_contains_product_in_context(self):
|
def test_detail_contains_product_in_context(self):
|
||||||
url = reverse("products:main_product_detail", kwargs={"main_slug": "visera"})
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "radiuma"})
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.context["main_product"], self.main_product)
|
self.assertEqual(response.context["main_product"], self.main_product)
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class MainProductDetailViewTest(ProductViewsSetup):
|
|||||||
def test_inactive_product_returns_404(self):
|
def test_inactive_product_returns_404(self):
|
||||||
self.main_product.is_active = False
|
self.main_product.is_active = False
|
||||||
self.main_product.save()
|
self.main_product.save()
|
||||||
url = reverse("products:main_product_detail", kwargs={"main_slug": "visera"})
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "radiuma"})
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ class SubProductDetailViewTest(ProductViewsSetup):
|
|||||||
def test_sub_detail_returns_200(self):
|
def test_sub_detail_returns_200(self):
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"products:sub_product_detail",
|
"products:sub_product_detail",
|
||||||
kwargs={"main_slug": "visera", "sub_slug": "image-processing"},
|
kwargs={"main_slug": "radiuma", "sub_slug": "image-processing"},
|
||||||
)
|
)
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -96,7 +96,7 @@ class SubProductDetailViewTest(ProductViewsSetup):
|
|||||||
def test_sub_detail_uses_correct_template(self):
|
def test_sub_detail_uses_correct_template(self):
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"products:sub_product_detail",
|
"products:sub_product_detail",
|
||||||
kwargs={"main_slug": "visera", "sub_slug": "image-processing"},
|
kwargs={"main_slug": "radiuma", "sub_slug": "image-processing"},
|
||||||
)
|
)
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertTemplateUsed(response, "products/sub_detail.html")
|
self.assertTemplateUsed(response, "products/sub_detail.html")
|
||||||
@@ -104,7 +104,7 @@ class SubProductDetailViewTest(ProductViewsSetup):
|
|||||||
def test_sub_detail_context_keys(self):
|
def test_sub_detail_context_keys(self):
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"products:sub_product_detail",
|
"products:sub_product_detail",
|
||||||
kwargs={"main_slug": "visera", "sub_slug": "image-processing"},
|
kwargs={"main_slug": "radiuma", "sub_slug": "image-processing"},
|
||||||
)
|
)
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertIn("main_product", response.context)
|
self.assertIn("main_product", response.context)
|
||||||
@@ -115,7 +115,7 @@ class SubProductDetailViewTest(ProductViewsSetup):
|
|||||||
def test_sub_detail_articles_in_context(self):
|
def test_sub_detail_articles_in_context(self):
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"products:sub_product_detail",
|
"products:sub_product_detail",
|
||||||
kwargs={"main_slug": "visera", "sub_slug": "image-processing"},
|
kwargs={"main_slug": "radiuma", "sub_slug": "image-processing"},
|
||||||
)
|
)
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertIn(self.article, list(response.context["articles"]))
|
self.assertIn(self.article, list(response.context["articles"]))
|
||||||
@@ -123,7 +123,7 @@ class SubProductDetailViewTest(ProductViewsSetup):
|
|||||||
def test_nonexistent_sub_slug_returns_404(self):
|
def test_nonexistent_sub_slug_returns_404(self):
|
||||||
url = reverse(
|
url = reverse(
|
||||||
"products:sub_product_detail",
|
"products:sub_product_detail",
|
||||||
kwargs={"main_slug": "visera", "sub_slug": "does-not-exist"},
|
kwargs={"main_slug": "radiuma", "sub_slug": "does-not-exist"},
|
||||||
)
|
)
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ from django.conf.urls.static import static
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
admin.site.site_header = "Tecvico Administration"
|
admin.site.site_header = "Radiuma Administration"
|
||||||
admin.site.site_title = "Tecvico Admin"
|
admin.site.site_title = "Radiuma Admin"
|
||||||
admin.site.index_title = "Welcome to Tecvico Administration"
|
admin.site.index_title = "Welcome to Radiuma Administration"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
|
|||||||
@@ -560,6 +560,59 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
transform: translateY(-7px) rotate(-45deg);
|
transform: translateY(-7px) rotate(-45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
SVG Blob Image Decorations
|
||||||
|
============================================================ */
|
||||||
|
.blob-img {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blob-img--hero-left {
|
||||||
|
width: 420px;
|
||||||
|
top: 5%;
|
||||||
|
left: -80px;
|
||||||
|
opacity: 0.18;
|
||||||
|
filter: blur(2px);
|
||||||
|
animation: blob-morph 16s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blob-img--hero-right {
|
||||||
|
width: 380px;
|
||||||
|
top: 15%;
|
||||||
|
right: -60px;
|
||||||
|
opacity: 0.13;
|
||||||
|
filter: blur(1px);
|
||||||
|
animation: blob-morph 20s ease-in-out infinite reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blob-img--page-hero {
|
||||||
|
width: 500px;
|
||||||
|
top: -50px;
|
||||||
|
right: -100px;
|
||||||
|
opacity: 0.15;
|
||||||
|
filter: blur(3px);
|
||||||
|
animation: blob-morph 18s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blob-img--page-corner {
|
||||||
|
width: 280px;
|
||||||
|
bottom: -30px;
|
||||||
|
left: 5%;
|
||||||
|
opacity: 0.1;
|
||||||
|
filter: blur(2px);
|
||||||
|
animation: blob-morph 22s ease-in-out infinite reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blob-img--downloads {
|
||||||
|
width: 600px;
|
||||||
|
top: -80px;
|
||||||
|
right: -120px;
|
||||||
|
opacity: 0.12;
|
||||||
|
filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Hero
|
Hero
|
||||||
============================================================ */
|
============================================================ */
|
||||||
@@ -583,8 +636,17 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
.hero-content {
|
.hero-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
max-width: 780px;
|
width: 100%;
|
||||||
|
max-width: var(--container-max);
|
||||||
padding-top: var(--spacing-2xl);
|
padding-top: var(--spacing-2xl);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-text {
|
||||||
|
max-width: 620px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-badge {
|
.hero-badge {
|
||||||
@@ -603,10 +665,10 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hero-title {
|
.hero-title {
|
||||||
font-size: clamp(2.8rem, 7vw, 5.5rem);
|
font-size: clamp(2rem, 4.5vw, 3.5rem);
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
line-height: 1.05;
|
line-height: 1.1;
|
||||||
letter-spacing: -0.04em;
|
letter-spacing: -0.03em;
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
animation: fade-slide-up 0.8s ease-out 0.2s both;
|
animation: fade-slide-up 0.8s ease-out 0.2s both;
|
||||||
}
|
}
|
||||||
@@ -1578,6 +1640,129 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
/* ============================================================
|
/* ============================================================
|
||||||
Contact Page
|
Contact Page
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
.contact-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 360px;
|
||||||
|
gap: 2.5rem;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-form-wrap {
|
||||||
|
padding: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-form-title {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.required-star {
|
||||||
|
color: var(--accent-blue-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.optional-label {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--text-muted, var(--text-secondary));
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input,
|
||||||
|
.form-group textarea,
|
||||||
|
.form-group select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.72rem 1rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus,
|
||||||
|
.form-group textarea:focus {
|
||||||
|
border-color: rgba(79, 142, 247, 0.5);
|
||||||
|
box-shadow: 0 0 0 3px rgba(79, 142, 247, 0.1);
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group.has-error input,
|
||||||
|
.form-group.has-error textarea {
|
||||||
|
border-color: rgba(239, 68, 68, 0.5);
|
||||||
|
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field-error {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: rgb(252, 165, 165);
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
min-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group--captcha {
|
||||||
|
background: rgba(79, 142, 247, 0.04);
|
||||||
|
border: 1px solid rgba(79, 142, 247, 0.15);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-input {
|
||||||
|
max-width: 130px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-submit {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-spinner svg {
|
||||||
|
animation: spin 0.9s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--sm {
|
||||||
|
padding: 0.45rem 1.1rem !important;
|
||||||
|
font-size: 0.85rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.contact-grid {
|
.contact-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
@@ -1585,7 +1770,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.contact-card {
|
.contact-card {
|
||||||
padding: 2.25rem;
|
padding: 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
@@ -1620,6 +1805,84 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Modal
|
||||||
|
============================================================ */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.72);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
-webkit-backdrop-filter: blur(6px);
|
||||||
|
z-index: 9000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-box {
|
||||||
|
max-width: 460px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 2.5rem;
|
||||||
|
text-align: center;
|
||||||
|
animation: modal-pop 0.22s ease-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modal-pop {
|
||||||
|
from { opacity: 0; transform: scale(0.92) translateY(10px); }
|
||||||
|
to { opacity: 1; transform: scale(1) translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-icon-wrap {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(79, 142, 247, 0.12);
|
||||||
|
border: 1px solid rgba(79, 142, 247, 0.2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 1.5rem;
|
||||||
|
color: var(--accent-blue-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-icon-wrap--success {
|
||||||
|
background: rgba(34, 197, 94, 0.12);
|
||||||
|
border-color: rgba(34, 197, 94, 0.2);
|
||||||
|
color: rgb(134, 239, 172);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-icon-wrap--error {
|
||||||
|
background: rgba(239, 68, 68, 0.12);
|
||||||
|
border-color: rgba(239, 68, 68, 0.2);
|
||||||
|
color: rgb(252, 165, 165);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
font-size: 0.93rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.65;
|
||||||
|
margin-bottom: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Footer
|
Footer
|
||||||
============================================================ */
|
============================================================ */
|
||||||
@@ -1902,7 +2165,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hero-title {
|
.hero-title {
|
||||||
font-size: clamp(2.2rem, 8vw, 3rem);
|
font-size: clamp(1.7rem, 6vw, 2.4rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-strip-inner {
|
.about-strip-inner {
|
||||||
@@ -1918,6 +2181,219 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Hero App Preview (Screenshot)
|
||||||
|
============================================================ */
|
||||||
|
.hero-app-preview {
|
||||||
|
position: relative;
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow:
|
||||||
|
0 32px 90px rgba(0, 0, 0, 0.65),
|
||||||
|
0 0 80px rgba(79, 142, 247, 0.22),
|
||||||
|
0 0 0 1px rgba(79, 142, 247, 0.12);
|
||||||
|
animation: hero-preview-in 1.1s ease-out 0.6s both;
|
||||||
|
align-self: center;
|
||||||
|
transform: perspective(1400px) rotateY(-14deg) rotateX(3deg);
|
||||||
|
transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1),
|
||||||
|
box-shadow 0.7s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-app-preview:hover {
|
||||||
|
transform: perspective(1400px) rotateY(-5deg) rotateX(1deg);
|
||||||
|
box-shadow:
|
||||||
|
0 40px 120px rgba(0, 0, 0, 0.7),
|
||||||
|
0 0 100px rgba(79, 142, 247, 0.32),
|
||||||
|
0 0 0 1px rgba(79, 142, 247, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hero-preview-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: perspective(1400px) rotateY(-20deg) rotateX(4deg) translateX(50px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: perspective(1400px) rotateY(-14deg) rotateX(3deg) translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-app-preview-glow {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(7, 7, 19, 0.88) 0%,
|
||||||
|
rgba(7, 7, 19, 0.45) 28%,
|
||||||
|
transparent 62%
|
||||||
|
);
|
||||||
|
z-index: 2;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-app-preview img {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
opacity: 0.15;
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-app-preview:hover img {
|
||||||
|
opacity: 0.42;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Screenshots Gallery
|
||||||
|
============================================================ */
|
||||||
|
.screenshots-section {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshots-section::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse 70% 50% at 50% 50%, rgba(79, 142, 247, 0.06) 0%, transparent 70%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshots-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-item {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
|
||||||
|
transition: var(--transition-smooth);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
cursor: zoom-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-item img {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
opacity: 0.82;
|
||||||
|
transition: opacity 0.35s ease, transform 0.45s ease;
|
||||||
|
aspect-ratio: 16/10;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-item:hover {
|
||||||
|
border-color: rgba(79, 142, 247, 0.3);
|
||||||
|
box-shadow:
|
||||||
|
0 16px 60px rgba(0, 0, 0, 0.6),
|
||||||
|
0 0 20px rgba(79, 142, 247, 0.12);
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-item:hover img {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* About page screenshots */
|
||||||
|
.about-screenshots-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.5fr 1fr 1fr;
|
||||||
|
gap: 1.25rem;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-screenshots-grid .screenshot-item img {
|
||||||
|
aspect-ratio: 16/10;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Platform Icons (Downloads)
|
||||||
|
============================================================ */
|
||||||
|
.platform-icon {
|
||||||
|
display: block;
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
opacity: 0.85;
|
||||||
|
transition: var(--transition-smooth);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-card:hover .platform-icon {
|
||||||
|
opacity: 1;
|
||||||
|
filter: brightness(0) invert(1) drop-shadow(0 0 8px rgba(79, 142, 247, 0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-card--unavailable .platform-icon {
|
||||||
|
opacity: 0.35;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-img {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Product Logo Image (fallback on product detail)
|
||||||
|
============================================================ */
|
||||||
|
.product-logo-img {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 280px;
|
||||||
|
height: auto;
|
||||||
|
object-fit: contain;
|
||||||
|
opacity: 0.9;
|
||||||
|
filter: drop-shadow(0 4px 20px rgba(79, 142, 247, 0.3));
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Responsive — Images
|
||||||
|
============================================================ */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.hero-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-app-preview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-screenshots-grid {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-screenshots-grid .screenshot-item--featured {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.blob-img--hero-left,
|
||||||
|
.blob-img--hero-right,
|
||||||
|
.blob-img--page-hero,
|
||||||
|
.blob-img--page-corner,
|
||||||
|
.blob-img--downloads {
|
||||||
|
opacity: 0.08;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-screenshots-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
:root {
|
:root {
|
||||||
--container-padding: 1rem;
|
--container-padding: 1rem;
|
||||||
@@ -1939,4 +2415,16 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
.contact-grid {
|
.contact-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.screenshots-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-box {
|
||||||
|
padding: 1.75rem 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="552.7" preserveAspectRatio="xMidYMid meet" version="1" viewBox="-44.4 -20.7 680.7 552.7" width="680.7" zoomAndPan="magnify"><g data-name="Layer 2"><g data-name="Layer 1" id="change1_1"><path d="M28.21,141.27c47.95-128.07,218-162,375-130C499.14,30.83,636.3,240.36,511.48,386.14s-317.73,139.9-396,31.74S-44.42,335.26,28.21,141.27Z" fill="#9cadce"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 520 B |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="503.1" preserveAspectRatio="xMidYMid meet" version="1" viewBox="40.9 -1.4 419.8 503.1" width="419.8" zoomAndPan="magnify"><g id="change1_1"><path d="M 58.464844 166.386719 C 40.9375 218.816406 47.953125 277.582031 70.871094 327.890625 C 93.789062 378.199219 131.609375 420.613281 174.550781 455.433594 C 202.554688 478.140625 234.988281 498.628906 271.015625 499.933594 C 321.9375 501.773438 365.957031 465.300781 399.199219 426.683594 C 431.734375 388.890625 460.777344 339.984375 448.472656 291.65625 C 435.9375 242.417969 385.121094 212.230469 364.699219 165.703125 C 342.992188 116.257812 354.605469 48.347656 310.761719 16.824219 C 289.902344 1.824219 262.472656 0.65625 236.789062 0.046875 C 178.226562 -1.351562 175.976562 30.410156 144.183594 68.980469 C 115.597656 103.65625 73.695312 120.832031 58.464844 166.386719" fill="#d1cfe2"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1013 B |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="503.1" preserveAspectRatio="xMidYMid meet" version="1" viewBox="40.9 -1.4 419.8 503.1" width="419.8" zoomAndPan="magnify"><g id="change1_1"><path d="M 58.464844 166.386719 C 40.9375 218.816406 47.953125 277.582031 70.871094 327.890625 C 93.789062 378.199219 131.609375 420.613281 174.550781 455.433594 C 202.554688 478.140625 234.988281 498.628906 271.015625 499.933594 C 321.9375 501.773438 365.957031 465.300781 399.199219 426.683594 C 431.734375 388.890625 460.777344 339.984375 448.472656 291.65625 C 435.9375 242.417969 385.121094 212.230469 364.699219 165.703125 C 342.992188 116.257812 354.605469 48.347656 310.761719 16.824219 C 289.902344 1.824219 262.472656 0.65625 236.789062 0.046875 C 178.226562 -1.351562 175.976562 30.410156 144.183594 68.980469 C 115.597656 103.65625 73.695312 120.832031 58.464844 166.386719" fill="#7ec4cf"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1013 B |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-name="Capa 1" fill="#000000" height="2952.1" preserveAspectRatio="xMidYMid meet" version="1" viewBox="-3.9 -20.1 3657.4 2952.1" width="3657.4" zoomAndPan="magnify"><g id="change1_1"><path d="M77.11,566.84C16.35,725,3.47,897.25.57,1066.66c-4.45,260,15.13,528.2,129.64,761.65,97.28,198.33,257.11,359,426.45,500.84,178.09,149.19,371.08,282.64,582.92,378,477.9,215.18,1023.91,224.89,1544.24,162.14,255-30.75,524.5-86.13,707.85-266C3626.48,2373,3653.51,2009,3652.19,1680.07c-1.38-342.79-25.87-709.39-232.92-982.6-251.06-331.27-697.11-433.95-1107-502.74C1861.38,119.08,1394.85,28.26,938.17,1.25,578-20.06,205.13,233.55,77.11,566.84Z" fill="#9cadce"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 796 B |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-name="Capa 1" fill="#000000" height="2952.1" preserveAspectRatio="xMidYMid meet" version="1" viewBox="-3.9 -20.1 3657.4 2952.1" width="3657.4" zoomAndPan="magnify"><g id="change1_1"><path d="M77.11,566.84C16.35,725,3.47,897.25.57,1066.66c-4.45,260,15.13,528.2,129.64,761.65,97.28,198.33,257.11,359,426.45,500.84,178.09,149.19,371.08,282.64,582.92,378,477.9,215.18,1023.91,224.89,1544.24,162.14,255-30.75,524.5-86.13,707.85-266C3626.48,2373,3653.51,2009,3652.19,1680.07c-1.38-342.79-25.87-709.39-232.92-982.6-251.06-331.27-697.11-433.95-1107-502.74C1861.38,119.08,1394.85,28.26,938.17,1.25,578-20.06,205.13,233.55,77.11,566.84Z" fill="#d1cfe2"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 796 B |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-name="Capa 1" fill="#000000" height="2952.1" preserveAspectRatio="xMidYMid meet" version="1" viewBox="-3.9 -20.1 3657.4 2952.1" width="3657.4" zoomAndPan="magnify"><g id="change1_1"><path d="M77.11,566.84C16.35,725,3.47,897.25.57,1066.66c-4.45,260,15.13,528.2,129.64,761.65,97.28,198.33,257.11,359,426.45,500.84,178.09,149.19,371.08,282.64,582.92,378,477.9,215.18,1023.91,224.89,1544.24,162.14,255-30.75,524.5-86.13,707.85-266C3626.48,2373,3653.51,2009,3652.19,1680.07c-1.38-342.79-25.87-709.39-232.92-982.6-251.06-331.27-697.11-433.95-1107-502.74C1861.38,119.08,1394.85,28.26,938.17,1.25,578-20.06,205.13,233.55,77.11,566.84Z" fill="#7ec4cf"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 796 B |
|
After Width: | Height: | Size: 454 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 834 B |
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="50.0" preserveAspectRatio="xMidYMid meet" version="1.0" viewBox="-0.1 0.0 50.1 50.0" width="50.1" zoomAndPan="magnify"><g id="change1_2"><circle cx="29" cy="16" fill="inherit" r="2"/></g><g id="change1_3"><circle cx="21" cy="16" fill="inherit" r="2"/></g><g id="change1_4"><path d="M25,26.016c-0.089,0-0.177-0.012-0.264-0.035c-0.136-0.037-3.342-0.937-4.69-4.631c-0.095-0.262-0.079-0.552,0.045-0.802 s0.347-0.437,0.613-0.518c2.081-0.632,3.664-1.824,3.68-1.836c0.363-0.275,0.868-0.275,1.231,0c0.016,0.012,1.598,1.204,3.679,1.836 c0.269,0.081,0.489,0.269,0.613,0.518c0.125,0.25,0.142,0.54,0.046,0.802c-1.35,3.695-4.555,4.596-4.69,4.631 C25.177,26.004,25.089,26.016,25,26.016z" fill="inherit"/></g><g id="change1_6"><path d="M47.809,29.493c-0.437-0.606-0.91-1.285-1.383-2c-0.158,2.958-0.593,5.649-1.303,8.052 c0.363,0.063,0.736,0.107,1.131,0.107c1.229,0,2.344-0.355,3.309-1.057c0.229-0.164,0.371-0.424,0.394-0.705 C50.053,32.618,49.109,31.309,47.809,29.493z" fill="inherit"/></g><g id="change1_7"><path d="M3.574,27.493c-0.473,0.715-0.946,1.394-1.381,2c-1.304,1.813-2.245,3.125-2.148,4.397 c0.021,0.281,0.166,0.539,0.394,0.705c0.965,0.7,2.078,1.057,3.307,1.057l0,0c0.393,0,0.768-0.047,1.13-0.107 C4.167,33.143,3.733,30.451,3.574,27.493z" fill="inherit"/></g><g id="change1_8"><path d="M42.157,46.029c-0.331-0.053-0.532-0.118-0.65-0.17c0.026-0.031,0.06-0.065,0.099-0.1c0.318-0.271,0.435-0.714,0.289-1.105 C41.749,44.261,41.375,44,40.956,44c-0.149,0-0.31,0.001-0.464,0.001c-2.792,3.021-6.521,4.935-11.129,5.655 C31.92,49.896,35.633,50,40.99,50c0.006-0.001,0.014,0,0.02,0c0.553,0,1-0.447,1-1c0-0.313-0.144-0.593-0.369-0.775 c-0.016-0.021-0.031-0.045-0.048-0.07c0.106-0.045,0.285-0.103,0.572-0.149c0.483-0.081,0.837-0.5,0.835-0.989 C42.998,46.525,42.643,46.107,42.157,46.029z" fill="inherit"/></g><g id="change1_1"><path d="M9.508,44.001C9.353,44.001,9.194,44,9.044,44c-0.419,0-0.793,0.261-0.938,0.654c-0.145,0.393-0.029,0.835,0.29,1.105 c0.039,0.033,0.072,0.067,0.098,0.1c-0.118,0.052-0.32,0.117-0.651,0.17C7.358,46.107,7.002,46.523,7,47.014 c-0.002,0.491,0.352,0.909,0.835,0.99c0.287,0.048,0.466,0.105,0.572,0.15C8.374,48.206,8.333,48.26,8.295,48.3 c-0.28,0.286-0.363,0.712-0.209,1.083C8.241,49.757,8.605,50,9.01,50c5.358,0,9.07-0.104,11.627-0.344 C16.031,48.936,12.3,47.021,9.508,44.001z" fill="inherit"/></g><g id="change1_5"><path d="M43.284,20.301c-0.365-1.122-0.778-2.387-0.854-2.867C42.393,6.11,33.432,0,25,0C16.568,0,7.608,6.11,7.571,17.434 c-0.077,0.479-0.488,1.743-0.854,2.863c-0.738,2.264-1.221,3.809-1.221,4.623C5.496,40.02,12.24,48,25,48s19.504-7.98,19.504-23.08 C44.504,24.106,44.021,22.563,43.284,20.301z M25,40c-3.608,0-12-1.163-12-11.932c0-4.426,1.629-6.599,1.641-6.615 c0.204-0.257,0.268-0.599,0.172-0.913c-0.02-0.063-1.897-6.345,0.516-9.604C16.294,9.634,17.822,9,20,9 c1.053,0,2.17,0.643,3.067,1.16c0.75,0.431,1.341,0.772,1.933,0.772c0.592,0,1.184-0.341,1.933-0.772C27.83,9.643,28.947,9,30,9 c2.177,0,3.703,0.633,4.669,1.934c2.408,3.248,0.536,9.544,0.517,9.608c-0.094,0.309-0.028,0.65,0.169,0.906 C35.37,21.47,37,23.643,37,28.068C37,38.837,28.608,40,25,40z" fill="inherit"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#545c62" height="33" id="Layer_1" preserveAspectRatio="xMidYMid meet" version="1.1" viewBox="0.0 0.0 263.5 33.0" width="263.5" x="0" xml:space="preserve" y="0" zoomAndPan="magnify"><g id="change1_1"><path d="M247.01,0H16.52C7.41,0,0,7.41,0,16.52c0,9.11,7.41,16.52,16.52,16.52h230.5c9.11,0,16.52-7.41,16.52-16.52 C263.53,7.41,256.12,0,247.01,0z M247.01,30.04H16.52C9.06,30.04,3,23.97,3,16.52C3,9.06,9.06,3,16.52,3h230.5 c7.46,0,13.52,6.06,13.52,13.52C260.53,23.97,254.47,30.04,247.01,30.04z" fill="inherit"/><path d="M6.2,16.52c0,5.69,4.63,10.32,10.32,10.32h1.42V6.2h-1.42C10.83,6.2,6.2,10.83,6.2,16.52z" fill="inherit"/><path d="M22.12 6.2H33.86V26.84H22.12z" fill="inherit"/><path d="M38.04 6.2H49.78V26.84H38.04z" fill="inherit"/><path d="M53.96 6.2H65.7V26.84H53.96z" fill="inherit"/><path d="M69.88 6.2H81.61999999999999V26.84H69.88z" fill="inherit"/><path d="M85.8 6.2H97.55V26.84H85.8z" fill="inherit"/><path d="M101.72 6.2H113.47V26.84H101.72z" fill="inherit"/><path d="M117.64 6.2H129.39V26.84H117.64z" fill="inherit"/><path d="M133.56 6.2H145.31V26.84H133.56z" fill="inherit"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="25.5" preserveAspectRatio="xMidYMid meet" version="1.0" viewBox="1.4 0.3 22.6 25.5" width="22.6" zoomAndPan="magnify"><g id="change1_1"><path d="M23.934,18.947c-0.598,1.324-0.884,1.916-1.652,3.086c-1.073,1.634-2.588,3.673-4.461,3.687 c-1.666,0.014-2.096-1.087-4.357-1.069c-2.261,0.011-2.732,1.089-4.4,1.072c-1.873-0.017-3.307-1.854-4.381-3.485 C1.68,17.663,1.363,12.301,3.219,9.448c1.313-2.023,3.391-3.211,5.342-3.211c1.987,0,3.236,1.092,4.879,1.092 c1.594,0,2.565-1.095,4.863-1.095c1.738,0,3.576,0.947,4.889,2.581C18.896,11.169,19.594,17.305,23.934,18.947z M16.559,4.408 c0.836-1.073,1.47-2.587,1.24-4.131c-1.364,0.093-2.959,0.964-3.891,2.092c-0.844,1.027-1.544,2.553-1.271,4.029 C14.125,6.446,15.665,5.559,16.559,4.408z" fill="inherit"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 878 B |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="18.8" preserveAspectRatio="xMidYMid meet" version="1.0" viewBox="1.3 2.6 21.3 18.8" width="21.3" zoomAndPan="magnify">
|
||||||
|
<g id="change1_1"><path d="m6.515 11.477a12.53 12.53 0 0 0 -2.959 0.642q-0.221 0.078-0.456 0.168l-1.767 6a10.292 10.292 0 0 1 3.218 -0.577 7.306 7.306 0 0 1 3.913 1.167c0.509-1.7 1.5-5.025 1.888-6.315-0.21-0.125-0.41-0.24-0.617-0.35a6.053 6.053 0 0 0 -3.22 -0.735z" fill="inherit"/></g>
|
||||||
|
<g id="change1_2"><path d="m12.492 14.051-1.8 6.128a9.29 9.29 0 0 0 3.728 1.226 10.9 10.9 0 0 0 3.916 -0.864l1.7-5.825a11.964 11.964 0 0 1 -3.252 0.485 7.98 7.98 0 0 1 -4.292 -1.15z" fill="inherit"/></g>
|
||||||
|
<g id="change1_3"><path d="m11.048 9.884c0.373-1.242 0.985-3.3 1.454-4.872l0.4-1.333a10.524 10.524 0 0 0 -2.1 -0.909 6.992 6.992 0 0 0 -1.583 -0.17 10.449 10.449 0 0 0 -3.55 0.782l-1.738 5.973a10.3 10.3 0 0 1 3.288 -0.623 7.318 7.318 0 0 1 3.829 1.152z" fill="inherit"/></g>
|
||||||
|
<g id="change1_4"><path d="m19.349 6.349a7.952 7.952 0 0 1 -4.239 -1.179l-1.835 6.222a7.983 7.983 0 0 0 7.634 0.307l1.758-5.931a10.389 10.389 0 0 1 -3.318 0.581z" fill="inherit"/></g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" clip-rule="evenodd" fill="#000000" fill-rule="evenodd" height="1345.5" image-rendering="optimizeQuality" preserveAspectRatio="xMidYMid meet" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" version="1" viewBox="-31.8 -166.3 1332.2 1345.5" width="1332.2" zoomAndPan="magnify"><g><g><g id="change1_1"><path d="M1050.7 613.2c42.3,64 40.2,151.2 -0.9,222.3 -40.1,69.5 -111.7,109.3 -186.3,111.8 -33.4,1.2 -61.4,18.8 -77,48.3 -96.6,183.6 -370.3,147.2 -416.3,-54.3 -10.8,-47.5 -57.2,-77.8 -104.9,-68.8 -50.8,9.6 -105.1,1.8 -153.4,-26.1 -107,-61.8 -143.7,-198.7 -81.9,-305.7 14.7,-25.4 33.5,-46.8 55.2,-63.9 30.7,-24.1 42.2,-63.2 29.6,-100.2 -133.8,-392.8 530.1,-542.9 581.6,-96.2 46.5,403 170.7,54.8 354.3,332.8z" fill="#9cadce"/></g><g id="change2_1"><path d="M1268.3 609.3c21.7,32.9 32.1,71.6 32,111.2 -0.2,40.3 -11.3,81.6 -32.7,118.6l0 0c-20.3,35.1 -48.4,62.9 -80.9,82.4 -33.7,20.3 -72.1,31.5 -111.3,32.9 -15.4,0.5 -29.6,4.8 -41.6,12.4 -12.1,7.6 -22.2,18.5 -29.3,32.1 -34.3,65.1 -90.1,103.4 -150.6,117.1 -27.4,6.3 -55.8,7.5 -83.6,3.9 -27.8,-3.7 -54.9,-12.2 -79.8,-25.3 -55,-28.9 -99.1,-80.3 -115.5,-151.8 -4.9,-21.8 -18.1,-39.7 -35.6,-51.2 -17.4,-11.4 -39.1,-16.3 -61.2,-12.2 -26.6,5.1 -54.2,5.5 -81.5,0.8 -26.2,-4.4 -52.2,-13.6 -76.6,-27.7l0 0c-55.2,-31.9 -92.3,-83.1 -107.6,-140.2 -15.3,-57 -8.8,-119.9 23.1,-175.1l0 0c7.4,-12.9 16,-24.8 25.4,-35.7 9.6,-11.2 20.2,-21.3 31.5,-30.2 14.2,-11.2 23.9,-25.7 28.7,-41.7 4.7,-16.1 4.5,-33.6 -1.4,-50.7 -44.5,-130.8 -2.7,-235.5 75.9,-301.5 40.5,-34 90.8,-57.7 144.1,-69.3 53.2,-11.5 109.4,-11 161.8,3.3 105.8,28.8 195.7,113.6 213.5,268.2 24.5,212.1 69.9,209.7 134.1,206.3 59.1,-3.1 132.9,-7 219.1,123.4zm17.9 111.2c0.1,-36.9 -9.6,-72.9 -29.7,-103.4 -81.7,-123.7 -151,-120 -206.6,-117.1 -72,3.8 -122.8,6.5 -148.8,-218.8 -17.1,-147.9 -102.6,-228.8 -203.2,-256.2 -50.1,-13.7 -104,-14.1 -155.1,-3 -51.1,11.1 -99.3,33.7 -138,66.3 -74.4,62.5 -113.9,161.7 -71.6,286.1 6.8,19.9 7.1,40.4 1.5,59.1 -5.5,18.8 -16.9,35.9 -33.4,48.9 -10.7,8.4 -20.6,17.9 -29.7,28.3 -8.8,10.2 -16.8,21.4 -23.8,33.6l0 -0.1c-29.9,51.9 -36,110.9 -21.6,164.5 14.3,53.6 49.1,101.7 101,131.6l-0.1 0c23,13.2 47.3,21.8 72,26 25.6,4.3 51.5,4 76.5,-0.7 25.8,-4.9 51.2,0.8 71.6,14.2 20.4,13.3 35.8,34.3 41.6,59.9 15.3,67.1 56.7,115.3 108.3,142.4 23.4,12.4 48.9,20.3 75,23.8 26.1,3.4 52.9,2.3 78.7,-3.6 56.7,-13 109,-48.9 141.2,-110 8.3,-15.9 20.1,-28.5 34.2,-37.4 14.2,-8.9 30.8,-14 48.7,-14.6 36.8,-1.3 72.9,-11.9 104.5,-30.9 30.5,-18.3 57,-44.4 76,-77.4l0 0c20.1,-34.9 30.6,-73.7 30.8,-111.5z" fill="#d1cfe2" fill-rule="nonzero"/></g></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 334 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 384 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 129 KiB |
@@ -64,15 +64,26 @@ function initMegaMenu() {
|
|||||||
const megamenu = productsItem.querySelector('.megamenu');
|
const megamenu = productsItem.querySelector('.megamenu');
|
||||||
if (!trigger || !megamenu) return;
|
if (!trigger || !megamenu) return;
|
||||||
|
|
||||||
productsItem.addEventListener('mouseenter', () => {
|
let hideTimer = null;
|
||||||
|
|
||||||
|
function openMenu() {
|
||||||
|
clearTimeout(hideTimer);
|
||||||
productsItem.classList.add('open');
|
productsItem.classList.add('open');
|
||||||
trigger.setAttribute('aria-expanded', 'true');
|
trigger.setAttribute('aria-expanded', 'true');
|
||||||
});
|
}
|
||||||
|
|
||||||
productsItem.addEventListener('mouseleave', () => {
|
function scheduleClose() {
|
||||||
productsItem.classList.remove('open');
|
hideTimer = setTimeout(() => {
|
||||||
trigger.setAttribute('aria-expanded', 'false');
|
productsItem.classList.remove('open');
|
||||||
});
|
trigger.setAttribute('aria-expanded', 'false');
|
||||||
|
}, 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
productsItem.addEventListener('mouseenter', openMenu);
|
||||||
|
productsItem.addEventListener('mouseleave', scheduleClose);
|
||||||
|
|
||||||
|
megamenu.addEventListener('mouseenter', openMenu);
|
||||||
|
megamenu.addEventListener('mouseleave', scheduleClose);
|
||||||
|
|
||||||
trigger.addEventListener('keydown', (e) => {
|
trigger.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
|||||||
@@ -4,8 +4,11 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="{% block meta_description %}Tecvico Corp — Advanced Medical Imaging & Radiomics Solutions{% endblock %}" />
|
<meta name="description" content="{% block meta_description %}Radiuma — Advanced Medical Imaging & Radiomics Solutions{% endblock %}" />
|
||||||
<title>{% block title %}Tecvico Corp{% endblock %} | Tecvico</title>
|
<title>{% block title %}Radiuma{% endblock %} | Radiuma</title>
|
||||||
|
<link rel="icon" href="{% static 'images/favicon-16.png' %}" type="image/png" sizes="16x16" />
|
||||||
|
<link rel="icon" href="{% static 'images/favicon-32.png' %}" type="image/png" sizes="32x32" />
|
||||||
|
<link rel="apple-touch-icon" href="{% static 'images/favicon-180.png' %}" sizes="180x180" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}What is Tecvico{% endblock %}
|
{% block title %}What is Radiuma{% endblock %}
|
||||||
{% block meta_description %}Learn about Tecvico Corp and ViSERA — Visualized & Standardized Environment for Radiomics Analysis, developed at UBC and BC Cancer Research Institute.{% endblock %}
|
{% block meta_description %}Learn about Radiuma and Radiuma — Visualized & Standardized Environment for Radiomics Analysis, developed at UBC and BC Cancer Research Institute.{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@@ -10,11 +10,13 @@
|
|||||||
<div class="page-hero-blobs" aria-hidden="true">
|
<div class="page-hero-blobs" aria-hidden="true">
|
||||||
<div class="blob blob--1"></div>
|
<div class="blob blob--1"></div>
|
||||||
<div class="blob blob--2"></div>
|
<div class="blob blob--2"></div>
|
||||||
|
<img src="{% static 'images/blob-blue-2.svg' %}" class="blob-img blob-img--page-hero" alt="" />
|
||||||
|
<img src="{% static 'images/abstract-shapes-2.svg' %}" class="blob-img blob-img--page-corner" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="page-hero-content">
|
<div class="page-hero-content">
|
||||||
<div class="section-badge">About</div>
|
<div class="section-badge">About</div>
|
||||||
<h1 class="page-hero-title" id="page-hero-heading">What is Tecvico?</h1>
|
<h1 class="page-hero-title" id="page-hero-heading">What is Radiuma?</h1>
|
||||||
<p class="page-hero-subtitle">
|
<p class="page-hero-subtitle">
|
||||||
Visualized & Standardized Environment for Radiomics Analysis
|
Visualized & Standardized Environment for Radiomics Analysis
|
||||||
</p>
|
</p>
|
||||||
@@ -49,7 +51,7 @@
|
|||||||
<div class="standard-badge">IBSI 1.0</div>
|
<div class="standard-badge">IBSI 1.0</div>
|
||||||
<h3 class="standard-title">Radiomic Feature Extraction</h3>
|
<h3 class="standard-title">Radiomic Feature Extraction</h3>
|
||||||
<p class="standard-desc">
|
<p class="standard-desc">
|
||||||
ViSERA is a python-based open-source package that enables standardized and
|
Radiuma is a python-based open-source package that enables standardized and
|
||||||
reproducible radiomic feature extraction in compliance with the Image Biomarker
|
reproducible radiomic feature extraction in compliance with the Image Biomarker
|
||||||
Standardization Initiative (IBSI 1.0).
|
Standardization Initiative (IBSI 1.0).
|
||||||
</p>
|
</p>
|
||||||
@@ -67,7 +69,7 @@
|
|||||||
<div class="standard-badge">Python</div>
|
<div class="standard-badge">Python</div>
|
||||||
<h3 class="standard-title">Open Source</h3>
|
<h3 class="standard-title">Open Source</h3>
|
||||||
<p class="standard-desc">
|
<p class="standard-desc">
|
||||||
ViSERA is a major, entirely-revamped upgrade to the original SERA (Matlab-based),
|
Radiuma is a major, entirely-revamped upgrade to the original SERA (Matlab-based),
|
||||||
now built on Python for broader accessibility and community contribution.
|
now built on Python for broader accessibility and community contribution.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -75,7 +77,7 @@
|
|||||||
<div class="standard-badge">End-to-End</div>
|
<div class="standard-badge">End-to-End</div>
|
||||||
<h3 class="standard-title">Standardized Workflows</h3>
|
<h3 class="standard-title">Standardized Workflows</h3>
|
||||||
<p class="standard-desc">
|
<p class="standard-desc">
|
||||||
ViSERA employs a number of popular image processing algorithms to create
|
Radiuma employs a number of popular image processing algorithms to create
|
||||||
end-to-end standardized workflows for consistent, reproducible research results.
|
end-to-end standardized workflows for consistent, reproducible research results.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,6 +85,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="section" aria-labelledby="screenshots-about-heading">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-badge">Interface</div>
|
||||||
|
<h2 class="section-title" id="screenshots-about-heading">Radiuma in Action</h2>
|
||||||
|
</div>
|
||||||
|
<div class="about-screenshots-grid">
|
||||||
|
<div class="screenshot-item screenshot-item--featured fade-in">
|
||||||
|
<img src="{% static 'images/screenshot-11.jpg' %}" alt="Radiuma multi-modal viewer" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
<div class="screenshot-item fade-in">
|
||||||
|
<img src="{% static 'images/screenshot-9.jpg' %}" alt="Radiuma settings panel" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
<div class="screenshot-item fade-in">
|
||||||
|
<img src="{% static 'images/screenshot-10.jpg' %}" alt="Radiuma analysis results" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="section" aria-labelledby="history-heading">
|
<section class="section" aria-labelledby="history-heading">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="history-block glass-card fade-in">
|
<div class="history-block glass-card fade-in">
|
||||||
@@ -90,12 +112,12 @@
|
|||||||
<div class="section-badge">History</div>
|
<div class="section-badge">History</div>
|
||||||
<h2 id="history-heading">Our Origins</h2>
|
<h2 id="history-heading">Our Origins</h2>
|
||||||
<p>
|
<p>
|
||||||
Tecvico (formerly known as Visera) has been developing since 2021 by the
|
Radiuma has been developing since 2021 by the
|
||||||
Quantitative Radiomolecular Imaging and Therapy (Qurit) lab & program at the
|
Quantitative Radiomolecular Imaging and Therapy (Qurit) lab & program at the
|
||||||
University of British Columbia & BC Cancer Research Institute, Vancouver, BC, Canada.
|
University of British Columbia & BC Cancer Research Institute, Vancouver, BC, Canada.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Our mission is to bridge the gap between cutting-edge radiomics research and
|
Radiuma's mission is to bridge the gap between cutting-edge radiomics research and
|
||||||
practical clinical application by providing standardized, reproducible, and
|
practical clinical application by providing standardized, reproducible, and
|
||||||
user-friendly software tools.
|
user-friendly software tools.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Contact{% endblock %}
|
{% block title %}Contact{% endblock %}
|
||||||
{% block meta_description %}Contact Tecvico Corp — support for ViSERA software and general inquiries.{% endblock %}
|
{% block meta_description %}Contact Radiuma — support for Radiuma software and general inquiries.{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@@ -15,66 +15,265 @@
|
|||||||
<div class="page-hero-content">
|
<div class="page-hero-content">
|
||||||
<div class="section-badge">Get in Touch</div>
|
<div class="section-badge">Get in Touch</div>
|
||||||
<h1 class="page-hero-title" id="contact-heading">Contact Us</h1>
|
<h1 class="page-hero-title" id="contact-heading">Contact Us</h1>
|
||||||
<p class="page-hero-subtitle">Support for ViSERA software is available via email and Discord.</p>
|
<p class="page-hero-subtitle">Have a question or want to reach the Radiuma team? Fill out the form or contact us directly below.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section" aria-labelledby="contact-details-heading">
|
<section class="section" aria-labelledby="contact-form-heading">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="sr-only" id="contact-details-heading">Contact Details</h2>
|
<div class="contact-layout">
|
||||||
<div class="contact-grid">
|
|
||||||
|
|
||||||
<div class="contact-card glass-card fade-in">
|
<div class="contact-form-wrap glass-card fade-in">
|
||||||
<div class="contact-icon" aria-hidden="true">
|
<h2 class="contact-form-title" id="contact-form-heading">Send a Message</h2>
|
||||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
|
<form id="contactForm" novalidate action="{% url 'pages:contact' %}" method="post">
|
||||||
<path d="M5 8C5 6.895 5.895 6 7 6H25C26.105 6 27 6.895 27 8V22C27 23.105 26.105 24 25 24H7C5.895 24 5 23.105 5 22V8Z" stroke="currentColor" stroke-width="1.5"/>
|
{% csrf_token %}
|
||||||
<path d="M5 9L16 16L27 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
<div class="form-group">
|
||||||
</div>
|
<label for="id_name">Full Name <span class="required-star">*</span></label>
|
||||||
<h3 class="contact-card-title">Email Support</h3>
|
{{ form.name }}
|
||||||
<p class="contact-card-desc">
|
<span class="form-field-error" data-field="name"></span>
|
||||||
For software support and general inquiries, reach us at:
|
</div>
|
||||||
</p>
|
|
||||||
<a href="mailto:support@tecvico.com" class="contact-email-link">
|
<div class="form-group">
|
||||||
support@tecvico.com
|
<label for="id_title">Subject / Title <span class="required-star">*</span></label>
|
||||||
</a>
|
{{ form.title }}
|
||||||
|
<span class="form-field-error" data-field="title"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="id_description">Message <span class="required-star">*</span></label>
|
||||||
|
{{ form.description }}
|
||||||
|
<span class="form-field-error" data-field="description"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="id_email">Email <span class="optional-label">(optional)</span></label>
|
||||||
|
{{ form.email }}
|
||||||
|
<span class="form-field-error" data-field="email"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group form-group--captcha">
|
||||||
|
<label for="id_captcha">
|
||||||
|
Verification: what is <strong id="captchaQuestion">{{ captcha_question }}</strong>?
|
||||||
|
<span class="required-star">*</span>
|
||||||
|
</label>
|
||||||
|
<input type="number" name="captcha_answer" id="id_captcha"
|
||||||
|
placeholder="Answer" min="0" max="18" required
|
||||||
|
class="captcha-input" />
|
||||||
|
<span class="form-field-error" data-field="captcha"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-primary btn-submit" id="submitBtn">
|
||||||
|
<span class="btn-label">Send Message</span>
|
||||||
|
<span class="btn-spinner" hidden aria-hidden="true">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="contact-card glass-card fade-in">
|
<aside class="contact-sidebar">
|
||||||
<div class="contact-icon" aria-hidden="true">
|
<div class="contact-card glass-card fade-in">
|
||||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor">
|
<div class="contact-icon" aria-hidden="true">
|
||||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057.1 18.08.114 18.1.133 18.11a19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/>
|
<svg width="28" height="28" viewBox="0 0 32 32" fill="none">
|
||||||
</svg>
|
<path d="M5 8C5 6.895 5.895 6 7 6H25C26.105 6 27 6.895 27 8V22C27 23.105 26.105 24 25 24H7C5.895 24 5 23.105 5 22V8Z" stroke="currentColor" stroke-width="1.5"/>
|
||||||
|
<path d="M5 9L16 16L27 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="contact-card-title">Email Support</h3>
|
||||||
|
<p class="contact-card-desc">For direct software support:</p>
|
||||||
|
<a href="mailto:support@visera.ca" class="contact-email-link">support@visera.ca</a>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="contact-card-title">Discord Community</h3>
|
|
||||||
<p class="contact-card-desc">
|
|
||||||
Join our Discord server for community support, feature discussions, and announcements.
|
|
||||||
</p>
|
|
||||||
<a href="https://discord.gg/9XxA6pV9hb" class="btn-primary" target="_blank" rel="noopener noreferrer">
|
|
||||||
Join Discord Forum
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="contact-card glass-card fade-in">
|
<div class="contact-card glass-card fade-in">
|
||||||
<div class="contact-icon" aria-hidden="true">
|
<div class="contact-icon" aria-hidden="true">
|
||||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
|
<svg width="28" height="28" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M16 3C11.029 3 7 7.029 7 12C7 19 16 29 16 29C16 29 25 19 25 12C25 7.029 20.971 3 16 3Z" stroke="currentColor" stroke-width="1.5"/>
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057.1 18.08.114 18.1.133 18.11a19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/>
|
||||||
<circle cx="16" cy="12" r="3" stroke="currentColor" stroke-width="1.5"/>
|
</svg>
|
||||||
</svg>
|
</div>
|
||||||
|
<h3 class="contact-card-title">Discord Community</h3>
|
||||||
|
<p class="contact-card-desc">Join for community support and announcements.</p>
|
||||||
|
<a href="https://discord.gg/9XxA6pV9hb" class="btn-primary btn--sm" target="_blank" rel="noopener noreferrer">Join Discord</a>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="contact-card-title">Office Address</h3>
|
|
||||||
<address class="contact-address">
|
<div class="contact-card glass-card fade-in">
|
||||||
<p>BC Cancer Research Center</p>
|
<div class="contact-icon" aria-hidden="true">
|
||||||
<p>675 West 10th Ave</p>
|
<svg width="28" height="28" viewBox="0 0 32 32" fill="none">
|
||||||
<p>Office 6-112</p>
|
<path d="M16 3C11.029 3 7 7.029 7 12C7 19 16 29 16 29C16 29 25 19 25 12C25 7.029 20.971 3 16 3Z" stroke="currentColor" stroke-width="1.5"/>
|
||||||
<p>Vancouver, BC, V5Z 1L3</p>
|
<circle cx="16" cy="12" r="3" stroke="currentColor" stroke-width="1.5"/>
|
||||||
<p>Canada</p>
|
</svg>
|
||||||
</address>
|
</div>
|
||||||
</div>
|
<h3 class="contact-card-title">Office</h3>
|
||||||
|
<address class="contact-address">
|
||||||
|
<p>BC Cancer Research Center</p>
|
||||||
|
<p>675 West 10th Ave, Office 6-112</p>
|
||||||
|
<p>Vancouver, BC, V5Z 1L3</p>
|
||||||
|
<p>Canada</p>
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<div id="noEmailModal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="noEmailTitle" hidden>
|
||||||
|
<div class="modal-box glass-card">
|
||||||
|
<div class="modal-icon-wrap">
|
||||||
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
|
<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="modal-title" id="noEmailTitle">No reply address</h3>
|
||||||
|
<p class="modal-body">You haven't provided an email address. Without it, we won't be able to respond to your message. Would you like to add one, or send without a reply address?</p>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="btn-ghost" id="modalAddEmail">Add Email</button>
|
||||||
|
<button class="btn-primary" id="modalSendAnyway">Send Without Email</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="successModal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="successTitle" hidden>
|
||||||
|
<div class="modal-box glass-card">
|
||||||
|
<div class="modal-icon-wrap modal-icon-wrap--success">
|
||||||
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
|
<path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="modal-title" id="successTitle">Message Sent!</h3>
|
||||||
|
<p class="modal-body">Thank you for reaching out. We've received your message and will get back to you as soon as possible.</p>
|
||||||
|
<button class="btn-primary" id="successClose">Done</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="errorModal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="errorTitle" hidden>
|
||||||
|
<div class="modal-box glass-card">
|
||||||
|
<div class="modal-icon-wrap modal-icon-wrap--error">
|
||||||
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||||
|
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="modal-title" id="errorTitle">Something Went Wrong</h3>
|
||||||
|
<p class="modal-body" id="errorModalMsg">Please check your answers and try again.</p>
|
||||||
|
<button class="btn-ghost" id="errorClose">Try Again</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const form = document.getElementById('contactForm');
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
const emailField = form.querySelector('#id_email');
|
||||||
|
const submitBtn = form.querySelector('#submitBtn');
|
||||||
|
const btnLabel = submitBtn.querySelector('.btn-label');
|
||||||
|
const btnSpinner = submitBtn.querySelector('.btn-spinner');
|
||||||
|
|
||||||
|
function showModal(id) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) { el.hidden = false; el.focus(); }
|
||||||
|
}
|
||||||
|
function hideModal(id) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) el.hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearErrors() {
|
||||||
|
form.querySelectorAll('.form-field-error').forEach(el => { el.textContent = ''; });
|
||||||
|
form.querySelectorAll('.form-group').forEach(el => el.classList.remove('has-error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function showErrors(errors) {
|
||||||
|
clearErrors();
|
||||||
|
Object.entries(errors).forEach(([field, msgs]) => {
|
||||||
|
const span = form.querySelector(`[data-field="${field}"]`);
|
||||||
|
if (span) {
|
||||||
|
span.textContent = msgs[0] || '';
|
||||||
|
span.closest('.form-group')?.classList.add('has-error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLoading(on) {
|
||||||
|
submitBtn.disabled = on;
|
||||||
|
btnLabel.hidden = on;
|
||||||
|
btnSpinner.hidden = !on;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitForm() {
|
||||||
|
clearErrors();
|
||||||
|
setLoading(true);
|
||||||
|
const formData = new FormData(form);
|
||||||
|
try {
|
||||||
|
const res = await fetch(form.action || window.location.href, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.success) {
|
||||||
|
form.reset();
|
||||||
|
showModal('successModal');
|
||||||
|
} else {
|
||||||
|
if (data.errors) showErrors(data.errors);
|
||||||
|
if (data.errors?.captcha) {
|
||||||
|
document.getElementById('errorModalMsg').textContent = data.errors.captcha[0];
|
||||||
|
showModal('errorModal');
|
||||||
|
} else if (!res.ok) {
|
||||||
|
showModal('errorModal');
|
||||||
|
}
|
||||||
|
if (data.captcha_question) {
|
||||||
|
document.getElementById('captchaQuestion').textContent = data.captcha_question;
|
||||||
|
const captchaInput = form.querySelector('#id_captcha');
|
||||||
|
if (captchaInput) captchaInput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
showModal('errorModal');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener('submit', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!emailField.value.trim()) {
|
||||||
|
showModal('noEmailModal');
|
||||||
|
} else {
|
||||||
|
submitForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('modalAddEmail')?.addEventListener('click', () => {
|
||||||
|
hideModal('noEmailModal');
|
||||||
|
emailField.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('modalSendAnyway')?.addEventListener('click', () => {
|
||||||
|
hideModal('noEmailModal');
|
||||||
|
submitForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('successClose')?.addEventListener('click', () => hideModal('successModal'));
|
||||||
|
document.getElementById('errorClose')?.addEventListener('click', () => hideModal('errorModal'));
|
||||||
|
|
||||||
|
document.querySelectorAll('.modal-overlay').forEach(overlay => {
|
||||||
|
overlay.addEventListener('click', e => {
|
||||||
|
if (e.target === overlay) overlay.hidden = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keydown', e => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
document.querySelectorAll('.modal-overlay').forEach(m => { m.hidden = true; });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Downloads{% endblock %}
|
{% block title %}Downloads{% endblock %}
|
||||||
{% block meta_description %}Download ViSERA by Tecvico — available for Windows, macOS, and Linux.{% endblock %}
|
{% block meta_description %}Download Radiuma by Radiuma — available for Windows, macOS, and Linux.{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@@ -10,12 +10,13 @@
|
|||||||
<div class="page-hero-blobs" aria-hidden="true">
|
<div class="page-hero-blobs" aria-hidden="true">
|
||||||
<div class="blob blob--1"></div>
|
<div class="blob blob--1"></div>
|
||||||
<div class="blob blob--2"></div>
|
<div class="blob blob--2"></div>
|
||||||
|
<img src="{% static 'images/modern-shape.svg' %}" class="blob-img blob-img--downloads" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="page-hero-content">
|
<div class="page-hero-content">
|
||||||
<div class="section-badge">Software</div>
|
<div class="section-badge">Software</div>
|
||||||
<h1 class="page-hero-title" id="downloads-heading">Downloads</h1>
|
<h1 class="page-hero-title" id="downloads-heading">Downloads</h1>
|
||||||
<p class="page-hero-subtitle">Download the latest version of ViSERA for your platform.</p>
|
<p class="page-hero-subtitle">Download the latest version of Radiuma for your platform.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -27,9 +28,7 @@
|
|||||||
|
|
||||||
<div class="download-card glass-card fade-in {% if not windows_items %}download-card--unavailable{% endif %}">
|
<div class="download-card glass-card fade-in {% if not windows_items %}download-card--unavailable{% endif %}">
|
||||||
<div class="download-platform-icon" aria-hidden="true">
|
<div class="download-platform-icon" aria-hidden="true">
|
||||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor">
|
<img src="{% static 'images/icon-windows.svg' %}" alt="Windows" width="56" height="56" class="platform-icon" />
|
||||||
<path d="M0 3.449L9.75 2.1v9.451H0m10.949-9.602L24 0v11.4H10.949M0 12.6h9.75v9.451L0 20.699M10.949 12.6H24V24l-12.9-1.801"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<h3 class="download-platform-name">Windows</h3>
|
<h3 class="download-platform-name">Windows</h3>
|
||||||
<p class="download-platform-desc">Windows 10 and up (64-bit)</p>
|
<p class="download-platform-desc">Windows 10 and up (64-bit)</p>
|
||||||
@@ -40,9 +39,7 @@
|
|||||||
{% if item.description %}<p class="download-item-desc">{{ item.description }}</p>{% endif %}
|
{% if item.description %}<p class="download-item-desc">{{ item.description }}</p>{% endif %}
|
||||||
{% if item.download_url and item.download_url != '#' %}
|
{% if item.download_url and item.download_url != '#' %}
|
||||||
<a href="{{ item.download_url }}" class="btn-primary download-btn" download>
|
<a href="{{ item.download_url }}" class="btn-primary download-btn" download>
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
|
<img src="{% static 'images/icon-download.svg' %}" width="18" height="18" alt="" aria-hidden="true" class="btn-icon-img" />
|
||||||
<path d="M9 2V12M9 12L5 8M9 12L13 8M3 15H15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -55,9 +52,7 @@
|
|||||||
|
|
||||||
<div class="download-card glass-card fade-in {% if not macos_items %}download-card--unavailable{% endif %}">
|
<div class="download-card glass-card fade-in {% if not macos_items %}download-card--unavailable{% endif %}">
|
||||||
<div class="download-platform-icon" aria-hidden="true">
|
<div class="download-platform-icon" aria-hidden="true">
|
||||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor">
|
<img src="{% static 'images/icon-macos.svg' %}" alt="macOS" width="56" height="56" class="platform-icon" />
|
||||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<h3 class="download-platform-name">macOS</h3>
|
<h3 class="download-platform-name">macOS</h3>
|
||||||
<p class="download-platform-desc">macOS 11 Big Sur and up</p>
|
<p class="download-platform-desc">macOS 11 Big Sur and up</p>
|
||||||
@@ -68,9 +63,7 @@
|
|||||||
{% if item.description %}<p class="download-item-desc">{{ item.description }}</p>{% endif %}
|
{% if item.description %}<p class="download-item-desc">{{ item.description }}</p>{% endif %}
|
||||||
{% if item.download_url and item.download_url != '#' %}
|
{% if item.download_url and item.download_url != '#' %}
|
||||||
<a href="{{ item.download_url }}" class="btn-primary download-btn" download>
|
<a href="{{ item.download_url }}" class="btn-primary download-btn" download>
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
|
<img src="{% static 'images/icon-download.svg' %}" width="18" height="18" alt="" aria-hidden="true" class="btn-icon-img" />
|
||||||
<path d="M9 2V12M9 12L5 8M9 12L13 8M3 15H15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -83,9 +76,7 @@
|
|||||||
|
|
||||||
<div class="download-card glass-card fade-in {% if not linux_items %}download-card--unavailable{% endif %}">
|
<div class="download-card glass-card fade-in {% if not linux_items %}download-card--unavailable{% endif %}">
|
||||||
<div class="download-platform-icon" aria-hidden="true">
|
<div class="download-platform-icon" aria-hidden="true">
|
||||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor">
|
<img src="{% static 'images/icon-linux.svg' %}" alt="Linux" width="56" height="56" class="platform-icon" />
|
||||||
<path d="M12.504 0c-.155 0-.315.008-.48.021-4.226.333-3.105 4.807-3.17 6.298-.076 1.092-.3 1.953-1.05 3.02-.885 1.051-2.127 2.75-2.716 4.521-.278.832-.41 1.684-.287 2.489a.424.424 0 00-.11.135c-.26.268-.45.6-.663.839-.199.199-.485.267-.797.4-.313.136-.658.269-.864.68-.09.189-.136.394-.132.602 0 .199.027.4.055.536.058.399.116.728.04.97-.249.68-.28 1.145-.106 1.484.174.334.535.47.94.601.81.2 1.91.135 2.774.6.926.466 1.866.67 2.616.47.526-.116.97-.464 1.208-.946.587-.003 1.23-.EraseINIT.3.337.572.95.23 1.681-.078.277-.21.557-.303.815-.125.35-.169.63-.046.875.315.648 1.086.698 1.728.hyperparameter698.256 0 .504-.034.72-.046.372-.022.626.026.783.17.428.39.405 1.353.504 2.408.096.99.184 2.023.618 2.813.22.391.528.717.938.939.41.22.915.316 1.44.282.52-.033 1.092-.218 1.544-.668.296-.296.522-.723.58-1.226.029-.248.017-.51-.02-.77-.15-1.168.056-2.22.617-2.82.274-.294.548-.452.748-.606.238-.186.427-.41.554-.676.247-.517.194-1.107-.026-1.633-.11-.271-.238-.517-.324-.773-.148-.448-.172-.913.053-1.377.226-.467.67-.876 1.02-1.428a.424.424 0 00.11-.134c.25-.43.31-.915.245-1.395-.065-.476-.245-.937-.49-1.37-.245-.436-.54-.838-.807-1.278-.254-.428-.47-.903-.522-1.445-.1-.994.268-2.1.368-3.158.05-.53.04-1.046-.059-1.518-.097-.47-.3-.9-.616-1.24-.636-.68-1.608-.904-2.527-.974-.45-.035-.899-.026-1.334.026zm.042 1.174c.42-.047.852-.056 1.277-.022.812.062 1.59.262 2.047.758.228.245.37.554.44.918.073.366.08.78.03 1.233-.103 1.055-.486 2.198-.36 3.34.061.567.302 1.09.575 1.56.27.46.572.876.822 1.319.25.443.43.904.483 1.362.053.45.003.895-.2 1.269a.37.37 0 00-.099.11c-.35.56-.83.993-1.1 1.57-.28.587-.295 1.218-.12 1.822.088.3.215.585.33.869.217.526.253 1.015.066 1.4a1.088 1.088 0 01-.354.43c-.22.162-.502.322-.806.638-.7.742-.924 1.963-.756 3.254.028.207.038.414.013.605-.045.38-.19.688-.41.908-.33.335-.765.497-1.178.524-.414.027-.83-.054-1.14-.22-.315-.169-.54-.43-.691-.714-.358-.647-.445-1.572-.54-2.553-.1-1.012-.118-2.066-.67-2.593-.269-.247-.645-.345-1.117-.32-.232.014-.455.047-.677.047-.44 0-.836-.054-.97-.33-.065-.133-.073-.36.043-.688.1-.29.238-.586.292-.866.133-.676.05-1.313-.313-1.75-.364-.44-1.027-.604-1.718-.777-.69-.176-1.44-.396-1.582-.862-.092-.302-.06-.666.11-1.127.087-.23.097-.482.054-.727-.083-.499.053-.986.22-1.427.246-.66 1.463-2.382 2.39-3.479.87-1.044 1.15-2.02 1.22-3.167.063-1.26-.744-5.02 2.57-5.27z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<h3 class="download-platform-name">Linux</h3>
|
<h3 class="download-platform-name">Linux</h3>
|
||||||
<p class="download-platform-desc">Ubuntu 20.04+ and compatible distributions</p>
|
<p class="download-platform-desc">Ubuntu 20.04+ and compatible distributions</p>
|
||||||
@@ -96,9 +87,7 @@
|
|||||||
{% if item.description %}<p class="download-item-desc">{{ item.description }}</p>{% endif %}
|
{% if item.description %}<p class="download-item-desc">{{ item.description }}</p>{% endif %}
|
||||||
{% if item.download_url and item.download_url != '#' %}
|
{% if item.download_url and item.download_url != '#' %}
|
||||||
<a href="{{ item.download_url }}" class="btn-primary download-btn" download>
|
<a href="{{ item.download_url }}" class="btn-primary download-btn" download>
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
|
<img src="{% static 'images/icon-download.svg' %}" width="18" height="18" alt="" aria-hidden="true" class="btn-icon-img" />
|
||||||
<path d="M9 2V12M9 12L5 8M9 12L13 8M3 15H15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
Download
|
Download
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -118,7 +107,7 @@
|
|||||||
<div class="info-block glass-card fade-in">
|
<div class="info-block glass-card fade-in">
|
||||||
<h2 id="reinstall-heading">Installation Notes</h2>
|
<h2 id="reinstall-heading">Installation Notes</h2>
|
||||||
<p>
|
<p>
|
||||||
If you have installed an older version of ViSERA, you can install the new version
|
If you have installed an older version of Radiuma, you can install the new version
|
||||||
over it without removing the previous installation. However, if you encounter any
|
over it without removing the previous installation. However, if you encounter any
|
||||||
problems, please remove the old version first before reinstalling.
|
problems, please remove the old version first before reinstalling.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}FAQ{% endblock %}
|
{% block title %}FAQ{% endblock %}
|
||||||
{% block meta_description %}Frequently asked questions about ViSERA by Tecvico — licensing, citation, system requirements, and more.{% endblock %}
|
{% block meta_description %}Frequently asked questions about Radiuma by Radiuma — licensing, citation, system requirements, and more.{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Home{% endblock %}
|
{% block title %}Home{% endblock %}
|
||||||
{% block meta_description %}Tecvico Corp — A Powerful Workflow Generator for Standardized Radiomics Analysis and Medical Image Visualization.{% endblock %}
|
{% block meta_description %}Radiuma — A Powerful Workflow Generator for Standardized Radiomics Analysis and Medical Image Visualization.{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@@ -11,25 +11,33 @@
|
|||||||
<div class="blob blob--1"></div>
|
<div class="blob blob--1"></div>
|
||||||
<div class="blob blob--2"></div>
|
<div class="blob blob--2"></div>
|
||||||
<div class="blob blob--3"></div>
|
<div class="blob blob--3"></div>
|
||||||
|
<img src="{% static 'images/blob-blue-1.svg' %}" class="blob-img blob-img--hero-left" alt="" />
|
||||||
|
<img src="{% static 'images/abstract-shapes.svg' %}" class="blob-img blob-img--hero-right" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div class="container hero-content">
|
<div class="container hero-content">
|
||||||
<div class="hero-badge">Developing since 2021</div>
|
<div class="hero-text">
|
||||||
<h1 class="hero-title" id="hero-heading">
|
<div class="hero-badge">Developing since 2021</div>
|
||||||
Tecvico,
|
<h1 class="hero-title" id="hero-heading">
|
||||||
<br />
|
Radiuma,
|
||||||
<span class="gradient-text">A Powerful Workflow Generator</span>
|
<br />
|
||||||
</h1>
|
<span class="gradient-text">A Powerful Workflow Generator</span>
|
||||||
<p class="hero-subtitle">
|
</h1>
|
||||||
for Standardized Radiomics Analysis and Medical Image Visualization
|
<p class="hero-subtitle">
|
||||||
</p>
|
for Standardized Radiomics Analysis and Medical Image Visualization
|
||||||
<p class="hero-description">
|
</p>
|
||||||
ViSERA is a free, open-source software specialized for visualization, processing,
|
<p class="hero-description">
|
||||||
segmentation, registration, fusion and analysis of medical and biomedical images,
|
Radiuma is a free, open-source software specialized for visualization, processing,
|
||||||
including radiomics and machine learning analysis.
|
segmentation, registration, fusion and analysis of medical and biomedical images,
|
||||||
</p>
|
including radiomics and machine learning analysis.
|
||||||
<div class="hero-actions">
|
</p>
|
||||||
<a href="{% url 'pages:downloads' %}" class="btn-primary">Download Now</a>
|
<div class="hero-actions">
|
||||||
<a href="{% url 'pages:about' %}" class="btn-ghost">About Tecvico</a>
|
<a href="{% url 'pages:downloads' %}" class="btn-primary">Download Now</a>
|
||||||
|
<a href="{% url 'pages:about' %}" class="btn-ghost">About Radiuma</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hero-app-preview fade-in">
|
||||||
|
<div class="hero-app-preview-glow" aria-hidden="true"></div>
|
||||||
|
<img src="{% static 'images/screenshot-1.jpg' %}" alt="Radiuma application — main workflow view" loading="eager" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -86,6 +94,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="section screenshots-section" aria-labelledby="screenshots-heading">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-badge">Gallery</div>
|
||||||
|
<h2 class="section-title" id="screenshots-heading">See Radiuma in Action</h2>
|
||||||
|
<p class="section-desc">
|
||||||
|
Explore Radiuma's powerful interface, workflow builder, and multi-modal image viewer.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="screenshots-grid">
|
||||||
|
<div class="screenshot-item fade-in">
|
||||||
|
<img src="{% static 'images/screenshot-2.jpg' %}" alt="Radiuma full workflow interface" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
<div class="screenshot-item fade-in">
|
||||||
|
<img src="{% static 'images/screenshot-3.png' %}" alt="Radiuma panel view" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
<div class="screenshot-item fade-in">
|
||||||
|
<img src="{% static 'images/screenshot-5.png' %}" alt="Radiuma segmentation view" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
<div class="screenshot-item fade-in">
|
||||||
|
<img src="{% static 'images/screenshot-8.jpg' %}" alt="Radiuma radiomics results" loading="lazy" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{% if nav_main_products %}
|
{% if nav_main_products %}
|
||||||
<section class="section products-section" aria-labelledby="products-heading">
|
<section class="section products-section" aria-labelledby="products-heading">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -123,14 +157,14 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<div class="section-badge">Value Proposition</div>
|
<div class="section-badge">Value Proposition</div>
|
||||||
<h2 class="section-title" id="problems-heading">What Problems Does Tecvico Solve?</h2>
|
<h2 class="section-title" id="problems-heading">What Problems Does Radiuma Solve?</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="problems-grid">
|
<div class="problems-grid">
|
||||||
<div class="problem-item glass-card fade-in">
|
<div class="problem-item glass-card fade-in">
|
||||||
<div class="problem-number" aria-hidden="true">01</div>
|
<div class="problem-number" aria-hidden="true">01</div>
|
||||||
<h3 class="problem-title">Accessibility</h3>
|
<h3 class="problem-title">Accessibility</h3>
|
||||||
<p class="problem-desc">
|
<p class="problem-desc">
|
||||||
Tecvico provides a user-friendly interface and a wide range of tools, allowing
|
Radiuma provides a user-friendly interface and a wide range of tools, allowing
|
||||||
researchers to perform complex data analysis without extensive technical knowledge
|
researchers to perform complex data analysis without extensive technical knowledge
|
||||||
or programming expertise.
|
or programming expertise.
|
||||||
</p>
|
</p>
|
||||||
@@ -139,7 +173,7 @@
|
|||||||
<div class="problem-number" aria-hidden="true">02</div>
|
<div class="problem-number" aria-hidden="true">02</div>
|
||||||
<h3 class="problem-title">Integrated Tools</h3>
|
<h3 class="problem-title">Integrated Tools</h3>
|
||||||
<p class="problem-desc">
|
<p class="problem-desc">
|
||||||
Tecvico integrates a vast collection of tools and resources from various domains
|
Radiuma integrates a vast collection of tools and resources from various domains
|
||||||
of healthcare and medical imaging research in a common, unified environment.
|
of healthcare and medical imaging research in a common, unified environment.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,7 +181,7 @@
|
|||||||
<div class="problem-number" aria-hidden="true">03</div>
|
<div class="problem-number" aria-hidden="true">03</div>
|
||||||
<h3 class="problem-title">Flexibility</h3>
|
<h3 class="problem-title">Flexibility</h3>
|
||||||
<p class="problem-desc">
|
<p class="problem-desc">
|
||||||
Tecvico offers flexibility in terms of tool optimization and workflow customization
|
Radiuma offers flexibility in terms of tool optimization and workflow customization
|
||||||
to match your specific research requirements.
|
to match your specific research requirements.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -171,7 +205,7 @@
|
|||||||
<div class="section-badge">Our Story</div>
|
<div class="section-badge">Our Story</div>
|
||||||
<h2 class="section-title" id="about-strip-heading">More to Know</h2>
|
<h2 class="section-title" id="about-strip-heading">More to Know</h2>
|
||||||
<p class="about-strip-text">
|
<p class="about-strip-text">
|
||||||
Tecvico has been developing since 2021 by the Quantitative Radiomolecular Imaging
|
Radiuma has been developing since 2021 by the Quantitative Radiomolecular Imaging
|
||||||
and Therapy (Qurit) lab & program at the University of British Columbia &
|
and Therapy (Qurit) lab & program at the University of British Columbia &
|
||||||
BC Cancer Research Institute, Vancouver, BC, Canada.
|
BC Cancer Research Institute, Vancouver, BC, Canada.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
<div class="footer-grid">
|
<div class="footer-grid">
|
||||||
|
|
||||||
<div class="footer-brand-col">
|
<div class="footer-brand-col">
|
||||||
<a href="{% url 'pages:home' %}" class="footer-logo" aria-label="Tecvico home">
|
<a href="{% url 'pages:home' %}" class="footer-logo" aria-label="Radiuma home">
|
||||||
<span class="brand-icon">T</span>
|
<span class="brand-icon">R</span>
|
||||||
<span class="brand-name">Tecvico</span>
|
<span class="brand-name">Radiuma</span>
|
||||||
</a>
|
</a>
|
||||||
<p class="footer-tagline">
|
<p class="footer-tagline">
|
||||||
Advancing medical imaging and radiomics research through innovative, standardized software solutions.
|
Advancing medical imaging and radiomics research through innovative, standardized software solutions.
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<h3 class="footer-heading">Navigation</h3>
|
<h3 class="footer-heading">Navigation</h3>
|
||||||
<ul class="footer-links" role="list">
|
<ul class="footer-links" role="list">
|
||||||
<li><a href="{% url 'pages:home' %}">Home</a></li>
|
<li><a href="{% url 'pages:home' %}">Home</a></li>
|
||||||
<li><a href="{% url 'pages:about' %}">What is Tecvico</a></li>
|
<li><a href="{% url 'pages:about' %}">What is Radiuma</a></li>
|
||||||
<li><a href="{% url 'products:overview' %}">Products</a></li>
|
<li><a href="{% url 'products:overview' %}">Products</a></li>
|
||||||
<li><a href="{% url 'pages:downloads' %}">Downloads</a></li>
|
<li><a href="{% url 'pages:downloads' %}">Downloads</a></li>
|
||||||
<li><a href="{% url 'pages:faq' %}">FAQ</a></li>
|
<li><a href="{% url 'pages:faq' %}">FAQ</a></li>
|
||||||
@@ -53,14 +53,14 @@
|
|||||||
<p>675 West 10th Ave, Office 6-112</p>
|
<p>675 West 10th Ave, Office 6-112</p>
|
||||||
<p>Vancouver, BC, V5Z 1L3</p>
|
<p>Vancouver, BC, V5Z 1L3</p>
|
||||||
</address>
|
</address>
|
||||||
<a href="mailto:support@tecvico.com" class="footer-email">support@tecvico.com</a>
|
<a href="mailto:support@visera.ca" class="footer-email">support@visera.ca</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p class="footer-copy">
|
<p class="footer-copy">
|
||||||
© {% now "Y" %} Tecvico Corp. All rights reserved. Formerly known as Visera.
|
© {% now "Y" %} Radiuma. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="footer-credit">
|
<p class="footer-credit">
|
||||||
Developed at <a href="https://www.qurit.ca" target="_blank" rel="noopener noreferrer">Qurit Lab</a>,
|
Developed at <a href="https://www.qurit.ca" target="_blank" rel="noopener noreferrer">Qurit Lab</a>,
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
<nav class="navbar" id="navbar" role="navigation" aria-label="Main navigation">
|
<nav class="navbar" id="navbar" role="navigation" aria-label="Main navigation">
|
||||||
<div class="navbar-container">
|
<div class="navbar-container">
|
||||||
|
|
||||||
<a href="{% url 'pages:home' %}" class="navbar-brand" aria-label="Tecvico home">
|
<a href="{% url 'pages:home' %}" class="navbar-brand" aria-label="Radiuma home">
|
||||||
<span class="brand-icon">T</span>
|
<span class="brand-icon">R</span>
|
||||||
<span class="brand-name">Tecvico</span>
|
<span class="brand-name">Radiuma</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<button class="navbar-toggle" id="navbarToggle" aria-expanded="false" aria-controls="navbarMenu" aria-label="Toggle navigation">
|
<button class="navbar-toggle" id="navbarToggle" aria-expanded="false" aria-controls="navbarMenu" aria-label="Toggle navigation">
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{% url 'pages:about' %}" class="nav-link {% if request.resolver_match.url_name == 'about' %}active{% endif %}">
|
<a href="{% url 'pages:about' %}" class="nav-link {% if request.resolver_match.url_name == 'about' %}active{% endif %}">
|
||||||
What is Tecvico
|
What is Radiuma
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
|
|
||||||
{% block title %}{{ main_product.name }}{% endblock %}
|
{% block title %}{{ main_product.name }}{% endblock %}
|
||||||
{% block meta_description %}{{ main_product.short_description }}{% endblock %}
|
{% block meta_description %}{{ main_product.short_description }}{% endblock %}
|
||||||
|
|
||||||
@@ -34,11 +35,13 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="product-detail-intro glass-card fade-in">
|
<div class="product-detail-intro glass-card fade-in">
|
||||||
<h2 class="sr-only" id="product-desc-heading">About {{ main_product.name }}</h2>
|
<h2 class="sr-only" id="product-desc-heading">About {{ main_product.name }}</h2>
|
||||||
{% if main_product.image %}
|
|
||||||
<div class="product-detail-image">
|
<div class="product-detail-image">
|
||||||
|
{% if main_product.image %}
|
||||||
<img src="{{ main_product.image.url }}" alt="{{ main_product.name }}" loading="lazy" />
|
<img src="{{ main_product.image.url }}" alt="{{ main_product.name }}" loading="lazy" />
|
||||||
|
{% else %}
|
||||||
|
<img src="{% static 'images/logo.png' %}" alt="{{ main_product.name }} logo" loading="lazy" class="product-logo-img" />
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
<div class="product-detail-text">
|
<div class="product-detail-text">
|
||||||
<p class="product-detail-desc">{{ main_product.description }}</p>
|
<p class="product-detail-desc">{{ main_product.description }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Products{% endblock %}
|
{% block title %}Products{% endblock %}
|
||||||
{% block meta_description %}Explore Tecvico's suite of medical imaging and radiomics software products.{% endblock %}
|
{% block meta_description %}Explore Radiuma's suite of medical imaging and radiomics software products.{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|||||||