voice fixes, custom voice for page, upload fixes, layoutbuilder, database extensions

This commit is contained in:
Adam 2025-06-02 10:48:20 +02:00
parent 94d002657a
commit 0fca13a505
41 changed files with 13214 additions and 250 deletions

View File

@ -1,5 +1,6 @@
<div class="top-row ps-3 navbar navbar-dark"> <div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid"> <div class="container-fluid">
@* <div style="float: left"> *@
<a class="navbar-brand" href="">BLAIzor</a> <a class="navbar-brand" href="">BLAIzor</a>
</div> </div>
</div> </div>

View File

@ -35,6 +35,29 @@
}); });
} }
} }
const hostSelector = 'elevenlabs-convai'; // Replace with your real tag name
const targetClass = '_poweredBy_1f9vw_251'; // Replace with your class
const intervalId = setInterval(() => {
const host = document.querySelector(hostSelector);
if (host && host.shadowRoot) {
const target = host.shadowRoot.querySelector(`.${targetClass}`);
if (target) {
target.style.setProperty('display', 'none', 'important');
console.log('Element found inside shadow DOM and hidden!');
clearInterval(intervalId);
} else {
console.log('Waiting for target inside shadowRoot...');
}
} else {
console.log('Waiting for host or shadowRoot...');
}
}, 500);
</script> </script>
<script src="https://elevenlabs.io/convai-widget/index.js" async type="text/javascript"></script> <script src="https://elevenlabs.io/convai-widget/index.js" async type="text/javascript"></script>
@ -59,6 +82,8 @@
</main> </main>
</div> </div>
@code { @code {
private string Menu = "Tanulás, Gyakorlás, Tesztelés, Vizsgázás"; private string Menu = "Tanulás, Gyakorlás, Tesztelés, Vizsgázás";

View File

@ -28,7 +28,7 @@
<NavMenu MenuString="@Menu" OnMenuClicked=@MenuClick></NavMenu> <NavMenu MenuString="@Menu" OnMenuClicked=@MenuClick></NavMenu>
<main> <main>
<article class="content container text-center" style="position: relative; z-index: 4;"> <article class="content text-center" style="position: relative; z-index: 4;">
<PageTitle>Home</PageTitle> <PageTitle>Home</PageTitle>
<VideoComponent SelectedBrandName="@selectedBrandName" /> <VideoComponent SelectedBrandName="@selectedBrandName" />
@* <HeadContent> @* <HeadContent>
@ -63,7 +63,17 @@
if(TTSEnabled) if(TTSEnabled)
{ {
<button class="btn btn-primary voicebutton" @onclick="MuteAI"><i class="fa-solid fa-volume-high"></i></button> if (!AiVoicePermitted)
{
<button data-hint="listen" class="btn btn-primary voicebutton" @onclick="AllowAIVoice">
<i class="fa-solid fa-volume-xmark"></i>
</button>
}
else
{
<button data-hint="listen" class="btn btn-primary voicebutton" @onclick="MuteAI"><i class="fa-solid fa-volume-high"></i></button>
}
<audio id="audioPlayer" hidden style="display: none;"></audio> <audio id="audioPlayer" hidden style="display: none;"></audio>
} }
} }
@ -78,7 +88,7 @@
</div> </div>
<p id="recordingText"></p> <p id="recordingText"></p>
<div class="row" id="currentContent"> <div id="currentContent">
@{ @{
if (!string.IsNullOrEmpty(HtmlContent.ToString())) if (!string.IsNullOrEmpty(HtmlContent.ToString()))
{ {
@ -184,22 +194,27 @@
private bool STTEnabled; private bool STTEnabled;
private bool _initVoicePending = false; private bool _initVoicePending = false;
private bool welcomeStage = true; private bool welcomeStage = true;
private bool AiVoicePermitted = false;
private string GetApiKey() => private string GetApiKey() =>
configuration?.GetSection("ElevenLabsAPI")?.GetValue<string>("ApiKey") ?? string.Empty; configuration?.GetSection("ElevenLabsAPI")?.GetValue<string>("ApiKey") ?? string.Empty;
private void AllowAIVoice()
{
AiVoicePermitted = true;
}
private void MuteAI() private void MuteAI()
{ {
TTSEnabled = false; AiVoicePermitted = false;
} }
private async Task ConvertTextToSpeech() private async Task ConvertTextToSpeech()
{ {
// string plainText = WebUtility.HtmlDecode(HtmlContent.ToString()); // string plainText = WebUtility.HtmlDecode(HtmlContent.ToString());
if (string.IsNullOrWhiteSpace(TextContent) || VoiceEnabled == false || TTSEnabled == false || welcomeStage) if (string.IsNullOrWhiteSpace(TextContent) || VoiceEnabled == false || TTSEnabled == false || welcomeStage || !AiVoicePermitted)
return; return;
Console.WriteLine("------------------------------OMGOMGOMG TTS call!!!!-------------"); Console.WriteLine("------------------------------OMGOMGOMG TTS call!!!!-------------");
@ -218,7 +233,15 @@
}; };
var requestJson = JsonSerializer.Serialize(requestContent); var requestJson = JsonSerializer.Serialize(requestContent);
string voiceId = "rE22Kc7UGoQj4zdHNYvd"; string voiceId;
if(SiteInfo.voiceId != null)
{
voiceId = SiteInfo.voiceId;
}
else
{
voiceId = "rE22Kc7UGoQj4zdHNYvd";
}
// string voiceId = "yyPLNYHg3CvjlSdSOdLh"; // string voiceId = "yyPLNYHg3CvjlSdSOdLh";
var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"https://api.elevenlabs.io/v1/text-to-speech/{voiceId}/stream") var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"https://api.elevenlabs.io/v1/text-to-speech/{voiceId}/stream")

View File

@ -17,7 +17,7 @@
<InputText class="form-control my-3" id="siteName" @bind-Value="siteInfo.SiteName" /> <InputText class="form-control my-3" id="siteName" @bind-Value="siteInfo.SiteName" />
</label> </label>
<label> <label>
Brand name: Site description:
<InputText class="form-control my-3" id="siteDescription" @bind-Value="siteInfo.SiteDescription" /> <InputText class="form-control my-3" id="siteDescription" @bind-Value="siteInfo.SiteDescription" />
</label> </label>
<label> <label>
@ -28,6 +28,10 @@
Default color Default color
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.DefaultColor" /> <InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.DefaultColor" />
</label> </label>
<label>
Entity
<InputText class="form-control my-3" id="defaultColor" @bind-Value="siteInfo.Entity" />
</label>
<label> <label>
Your domain url Your domain url
<InputText class="form-control my-3" id="domainUrl" @bind-Value="siteInfo.DomainUrl" /> <InputText class="form-control my-3" id="domainUrl" @bind-Value="siteInfo.DomainUrl" />

View File

@ -28,7 +28,7 @@
<NavMenu MenuString="@Menu" OnMenuClicked=@MenuClick></NavMenu> <NavMenu MenuString="@Menu" OnMenuClicked=@MenuClick></NavMenu>
<main> <main>
<article class="content container text-center" style="position: relative; z-index: 4;"> <article class="content text-center" style="position: relative; z-index: 4;">
<PageTitle>Home</PageTitle> <PageTitle>Home</PageTitle>
<VideoComponent SelectedBrandName="@selectedBrandName" /> <VideoComponent SelectedBrandName="@selectedBrandName" />
@* <HeadContent> @* <HeadContent>
@ -66,7 +66,16 @@
if(TTSEnabled) if(TTSEnabled)
{ {
<button data-hint="listen" class="btn btn-primary voicebutton" @onclick="ConvertTextToSpeech"><i class="fa-solid fa-volume-high"></i></button> if(!AiVoicePermitted)
{
<button data-hint="listen" class="btn btn-primary voicebutton" @onclick="AllowAIVoice"><i class="fa-solid fa-volume-xmark"></i>
</button>
}
else
{
<button data-hint="listen" class="btn btn-primary voicebutton" @onclick="MuteAI"><i class="fa-solid fa-volume-high"></i></button>
}
<audio id="audioPlayer" hidden style="display: none;"></audio> <audio id="audioPlayer" hidden style="display: none;"></audio>
} }
@ -84,7 +93,7 @@
<p id="recordingText"></p> <p id="recordingText"></p>
<div class="row" id="currentContent"> <div id="currentContent">
@{ @{
if (!string.IsNullOrEmpty(HtmlContent.ToString())) if (!string.IsNullOrEmpty(HtmlContent.ToString()))
{ {
@ -191,20 +200,25 @@
private bool STTEnabled; private bool STTEnabled;
private bool _initVoicePending = false; private bool _initVoicePending = false;
private bool welcomeStage = true; private bool welcomeStage = true;
private bool AiVoicePermitted = false;
private string GetApiKey() => private string GetApiKey() =>
configuration?.GetSection("ElevenLabsAPI")?.GetValue<string>("ApiKey") ?? string.Empty; configuration?.GetSection("ElevenLabsAPI")?.GetValue<string>("ApiKey") ?? string.Empty;
private void AllowAIVoice()
{
AiVoicePermitted = true;
}
private void MuteAI() private void MuteAI()
{ {
TTSEnabled = false; AiVoicePermitted = false;
} }
private async Task ConvertTextToSpeech() private async Task ConvertTextToSpeech()
{ {
// string plainText = WebUtility.HtmlDecode(HtmlContent.ToString()); // string plainText = WebUtility.HtmlDecode(HtmlContent.ToString());
if (string.IsNullOrWhiteSpace(TextContent) || VoiceEnabled == false || TTSEnabled == false || welcomeStage) if (string.IsNullOrWhiteSpace(TextContent) || VoiceEnabled == false || TTSEnabled == false || welcomeStage || !AiVoicePermitted)
return; return;
Console.WriteLine("------------------------------OMGOMGOMG TTS call!!!!-------------"); Console.WriteLine("------------------------------OMGOMGOMG TTS call!!!!-------------");
@ -223,7 +237,15 @@
}; };
var requestJson = JsonSerializer.Serialize(requestContent); var requestJson = JsonSerializer.Serialize(requestContent);
string voiceId = "rE22Kc7UGoQj4zdHNYvd"; string voiceId;
if (SiteInfo.voiceId != null)
{
voiceId = SiteInfo.voiceId;
}
else
{
voiceId = "rE22Kc7UGoQj4zdHNYvd";
}
// string voiceId = "yyPLNYHg3CvjlSdSOdLh"; // string voiceId = "yyPLNYHg3CvjlSdSOdLh";
var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"https://api.elevenlabs.io/v1/text-to-speech/{voiceId}/stream") var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"https://api.elevenlabs.io/v1/text-to-speech/{voiceId}/stream")

View File

@ -12,9 +12,9 @@
@inject CustomAuthenticationStateProvider CustomAuthProvider @inject CustomAuthenticationStateProvider CustomAuthProvider
@inject IJSRuntime JSRuntime @inject IJSRuntime JSRuntime
<h3>Your Sites</h3> <h1>Your Sites</h1>
<div class="row g-0"> <div class="row g-0">
<RadzenPanel Collapsed="true" AllowCollapse="true" class="rz-my-10 rz-mx-auto" Style="width: 100%" <RadzenPanel Collapsed="true" AllowCollapse="true" class="rz-my-5 rz-mx-auto" Style="width: 100%"
Expand=@(() => Change("Panel expanded")) Collapse=@(() => Change("Panel collapsed"))> Expand=@(() => Change("Panel expanded")) Collapse=@(() => Change("Panel collapsed"))>
<HeaderTemplate> <HeaderTemplate>
<RadzenText TextStyle="TextStyle.H6" class="rz-display-flex rz-align-items-center rz-m-0"> <RadzenText TextStyle="TextStyle.H6" class="rz-display-flex rz-align-items-center rz-m-0">
@ -43,6 +43,10 @@
<label for="domainUrl">Domain URL</label> <label for="domainUrl">Domain URL</label>
<InputText id="domainUrl" placeholder="Domain url" class="form-control" @bind-Value="newSite.DomainUrl" /> <InputText id="domainUrl" placeholder="Domain url" class="form-control" @bind-Value="newSite.DomainUrl" />
</div> </div>
<div class="col-12 col-md-12 mb-3">
<label for="siteName" class="form-label">Site description</label>
<InputText id="siteDescription" placeholder="A description so AI will know what is this site about" class="form-control" @bind-Value="newSite.SiteDescription" />
</div>
</div> </div>
<button type="submit" class="btn btn-primary">Create Site</button> <button type="submit" class="btn btn-primary">Create Site</button>
@ -58,11 +62,11 @@
@if (siteInfoList.Any()) @if (siteInfoList.Any())
{ {
<RadzenPanel AllowCollapse="true" class="rz-my-10 rz-mx-auto" Style="width: 100%" <RadzenPanel AllowCollapse="true" class="rz-my-5 rz-mx-auto" Style="width: 100%"
Expand=@(() => Change("Panel expanded")) Collapse=@(() => Change("Panel collapsed"))> Expand=@(() => Change("Panel expanded")) Collapse=@(() => Change("Panel collapsed"))>
<HeaderTemplate> <HeaderTemplate>
<RadzenText TextStyle="TextStyle.H6" class="rz-display-flex rz-align-items-center rz-m-0"> <RadzenText TextStyle="TextStyle.H6" class="rz-display-flex rz-align-items-center rz-m-0">
<RadzenIcon Icon="account_box" class="rz-me-1" /><b>Your sites</b> <RadzenIcon Icon="account_box" class="rz-me-1" /><b>Sites created</b>
</RadzenText> </RadzenText>
</HeaderTemplate> </HeaderTemplate>
<ChildContent> <ChildContent>

View File

@ -10,7 +10,8 @@
@* <nav class="navbar navbar-expand-lg bg-body-tertiary" style="z-index: 5"> *@ @* <nav class="navbar navbar-expand-lg bg-body-tertiary" style="z-index: 5"> *@
<nav class="navbar fixed-top" style="z-index: 5"> <nav class="navbar fixed-top" style="z-index: 5">
<div class="container-fluid"> @* <div class="container-fluid"> *@
<div style="--bs-gutter-x: 1.5rem; --bs-gutter-y: 0; width: 100%; padding-right: calc(var(--bs-gutter-x) * .5); padding-left: calc(var(--bs-gutter-x) * .5); margin-right: auto; margin-left: auto;">
@{ @{
var brandFileName = _scopedContentService.SelectedDocument; var brandFileName = _scopedContentService.SelectedDocument;
var brandName = _scopedContentService.SelectedBrandName; var brandName = _scopedContentService.SelectedBrandName;

View File

@ -0,0 +1,637 @@
// <auto-generated />
using System;
using BLAIzor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace BLAIzor.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250602055410_addVoiceInformation")]
partial class addVoiceInformation
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("CssContent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("DesignTemplateId")
.HasColumnType("int");
b.Property<DateTime>("LastUpdated")
.HasColumnType("datetime2");
b.HasKey("Id");
b.HasIndex("DesignTemplateId")
.IsUnique();
b.ToTable("CssTemplates");
});
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeprecated")
.HasColumnType("bit");
b.Property<bool>("IsPrivate")
.HasColumnType("bit");
b.Property<bool>("IsPublished")
.HasColumnType("bit");
b.Property<string>("QDrandCollectionName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Tags")
.HasColumnType("nvarchar(max)");
b.Property<string>("TemplateName")
.HasColumnType("nvarchar(max)");
b.Property<string>("TemplatePhotoUrl")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("datetime2");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("Version")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("DesignTemplates");
b.HasData(
new
{
Id = 1,
CreatedAt = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Description = "The default template",
IsDeprecated = false,
IsPrivate = false,
IsPublished = false,
QDrandCollectionName = "html_snippets",
Status = "Draft",
Tags = "system",
TemplateName = "Default Site",
TemplatePhotoUrl = "/images/default-logo.png",
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
Version = 1
});
});
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("JsonDefinition")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("SiteInfoId")
.HasColumnType("int");
b.Property<string>("Slug")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<int>("Version")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("SiteInfoId", "Slug")
.IsUnique();
b.ToTable("FormDefinitions");
});
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("PointId")
.HasColumnType("int");
b.Property<Guid?>("QdrantPointId")
.HasColumnType("uniqueidentifier");
b.Property<bool>("ShowInMainMenu")
.HasColumnType("bit");
b.Property<int>("SiteInfoId")
.HasColumnType("int");
b.Property<int>("SortOrder")
.HasColumnType("int");
b.Property<string>("StoredHtml")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("SiteInfoId");
b.ToTable("MenuItems");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("BrandLogoUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultColor")
.HasColumnType("nvarchar(max)");
b.Property<string>("DefaultUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("DomainUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Entity")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsPublished")
.HasColumnType("bit");
b.Property<string>("Persona")
.HasColumnType("nvarchar(max)");
b.Property<bool>("STTActive")
.HasColumnType("bit");
b.Property<string>("SiteDescription")
.HasColumnType("nvarchar(max)");
b.Property<string>("SiteName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TTSActive")
.HasColumnType("bit");
b.Property<int?>("TemplateId")
.HasColumnType("int");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<string>("voiceId")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("TemplateId");
b.HasIndex("UserId");
b.ToTable("SiteInfos");
b.HasData(
new
{
Id = 1,
BrandLogoUrl = "/images/default-logo.png",
DefaultColor = "#FFFFFF",
DefaultUrl = "https://ai.poppixel.cloud",
DomainUrl = "poppixel.cloud",
IsPublished = false,
STTActive = false,
SiteName = "Default Site",
TTSActive = false,
TemplateId = 1,
UserId = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
b.HasData(
new
{
Id = "0988758e-e16c-4c2c-8c1e-efa3ac5f0274",
AccessFailedCount = 0,
ConcurrencyStamp = "a2836246-0303-4370-b283-e53a9a3f2813",
Email = "adam.g@aycode.com",
EmailConfirmed = true,
LockoutEnabled = false,
NormalizedEmail = "ADAM.G@AYCODE.COM",
NormalizedUserName = "ADAM.G@AYCODE.COM",
PasswordHash = "AQAAAAIAAYagAAAAEChxKCu+ReGvcZFR/6kPASbpnQdMp1MJuepeRyR4bfHTkUk8SfNAqmckGXvuw+GaGA==",
PhoneNumberConfirmed = false,
SecurityStamp = "7ecf121a-b0e7-4e30-a1f1-299eeaf0a9cc",
TwoFactorEnabled = false,
UserName = "adam.g@aycode.com"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("BLAIzor.Models.CssTemplate", b =>
{
b.HasOne("BLAIzor.Models.DesignTemplate", "DesignTemplate")
.WithOne("CssTemplate")
.HasForeignKey("BLAIzor.Models.CssTemplate", "DesignTemplateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("DesignTemplate");
});
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("BLAIzor.Models.FormDefinition", b =>
{
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
.WithMany("FormDefinitions")
.HasForeignKey("SiteInfoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SiteInfo");
});
modelBuilder.Entity("BLAIzor.Models.MenuItem", b =>
{
b.HasOne("BLAIzor.Models.SiteInfo", "SiteInfo")
.WithMany("MenuItems")
.HasForeignKey("SiteInfoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SiteInfo");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.HasOne("BLAIzor.Models.DesignTemplate", "Template")
.WithMany("Sites")
.HasForeignKey("TemplateId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Template");
b.Navigation("User");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("BLAIzor.Models.DesignTemplate", b =>
{
b.Navigation("CssTemplate")
.IsRequired();
b.Navigation("Sites");
});
modelBuilder.Entity("BLAIzor.Models.SiteInfo", b =>
{
b.Navigation("FormDefinitions");
b.Navigation("MenuItems");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,55 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BLAIzor.Migrations
{
/// <inheritdoc />
public partial class addVoiceInformation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Entity",
table: "SiteInfos",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Persona",
table: "SiteInfos",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "voiceId",
table: "SiteInfos",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.UpdateData(
table: "SiteInfos",
keyColumn: "Id",
keyValue: 1,
columns: new[] { "Entity", "Persona", "voiceId" },
values: new object[] { null, null, null });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Entity",
table: "SiteInfos");
migrationBuilder.DropColumn(
name: "Persona",
table: "SiteInfos");
migrationBuilder.DropColumn(
name: "voiceId",
table: "SiteInfos");
}
}
}

View File

@ -225,9 +225,15 @@ namespace BLAIzor.Migrations
.IsRequired() .IsRequired()
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
b.Property<string>("Entity")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsPublished") b.Property<bool>("IsPublished")
.HasColumnType("bit"); .HasColumnType("bit");
b.Property<string>("Persona")
.HasColumnType("nvarchar(max)");
b.Property<bool>("STTActive") b.Property<bool>("STTActive")
.HasColumnType("bit"); .HasColumnType("bit");
@ -247,6 +253,9 @@ namespace BLAIzor.Migrations
.IsRequired() .IsRequired()
.HasColumnType("nvarchar(450)"); .HasColumnType("nvarchar(450)");
b.Property<string>("voiceId")
.HasColumnType("nvarchar(max)");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("TemplateId"); b.HasIndex("TemplateId");

View File

@ -18,7 +18,9 @@
public string Tags { get; set; } public string Tags { get; set; }
public string Type { get; set; } // e.g. "article", "form", "gallery" public string Type { get; set; } // e.g. "article", "form", "gallery"
public string? Variant { get; set; } // 👈 NEW: e.g. "image-left", "image-right", "no-image" public string? Variant { get; set; } // 👈 NEW: e.g. "image-left", "image-right", "no-image"
public string SampleHtml { get; set; } public string SampleHtml { get; set; } //for design purposes
public List<string> Slots { get; set; } // e.g. ["title", "subtitle", "image", "cta"]
} }
} }

28
Models/LayoutBlock.cs Normal file
View File

@ -0,0 +1,28 @@
namespace BLAIzor.Models
{
public class LayoutBlock
{
public string Type { get; set; } // e.g. "hero", "article", "gallery", "testimonial"
public string? Variant { get; set; } // e.g. "image-left", "image-right", "no-image"
public List<string> RequiredSlots { get; set; } // e.g. ["title", "text", "image", "cta"]
public Dictionary<string, string> ContentMap { get; set; }
// Maps slot names to content IDs or keys, e.g. { "title": "main_heading", "image": "img_1" }
public int? PreferredSnippetId { get; set; }
// Optional: used if a perfect match snippet is found or AI prefers one
//public string? BootstrapStructure { get; set; }
// Optional: e.g. "row-cols-2", "container-fluid py-5"
public string RawContent { get; set; } = string.Empty;
public int Order { get; set; }
// The position of the block in the layout
public string? Notes { get; set; }
// Optional field for AI hints, comments, or debug info
}
}

8
Models/LayoutPlan.cs Normal file
View File

@ -0,0 +1,8 @@
namespace BLAIzor.Models
{
public class LayoutPlan
{
public string Title { get; set; }
public List<LayoutBlock> Blocks { get; set; } = new();
}
}

View File

@ -27,6 +27,10 @@ namespace BLAIzor.Models
public int? TemplateId { get; set; } public int? TemplateId { get; set; }
public bool TTSActive { get; set; } = false; public bool TTSActive { get; set; } = false;
public bool STTActive { get; set; } = false; public bool STTActive { get; set; } = false;
public string? voiceId { get; set; }
public string? Persona { get; set; }
public string? Entity { get; set; }
// Navigation property for IdentityUser // Navigation property for IdentityUser
public IdentityUser User { get; set; } public IdentityUser User { get; set; }

View File

@ -17,6 +17,11 @@ using Microsoft.AspNetCore.Mvc.Formatters;
using Google.Api; using Google.Api;
using System.CodeDom; using System.CodeDom;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization;
using BLAIzor.Services;
using Microsoft.DotNet.Scaffolding.Shared;
using System.Xml.XPath;
using Azure.Identity;
namespace BLAIzor.Services namespace BLAIzor.Services
{ {
@ -104,7 +109,7 @@ namespace BLAIzor.Services
public async Task GetChatGptWelcomeMessage(string sessionId, int SiteId, string menuList = "") public async Task GetChatGptWelcomeMessage(string sessionId, int SiteId, string menuList = "")
{ {
string currentUri = _navigationManager.Uri; string currentUri = _navigationManager.Uri;
Console.Write($"\n\n SessionId: {sessionId}\n\n"); //Console.Write($"\n\n SessionId: {sessionId}\n\n");
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument); //string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
_apiKey = GetApiKey(); _apiKey = GetApiKey();
@ -115,15 +120,15 @@ namespace BLAIzor.Services
if (selectedPoint != null) if (selectedPoint != null)
{ {
extractedText = selectedPoint.result.payload.content; extractedText = selectedPoint.result.payload.content;
//Console.Write($"\n -------------------------------- Found point: {selectedPoint.result.payload.content} \n"); ////Console.Write($"\n -------------------------------- Found point: {selectedPoint.result.payload.content} \n");
Console.Write($"\n -------------------------------- Found point: {selectedPoint.result.id} \n"); //Console.Write($"\n -------------------------------- Found point: {selectedPoint.result.id} \n");
} }
string systemMessage = "You are a helpful, " + Mood + " assistant that welcomes the user in the name of the brand or person described by the content, on a website of " + _scopedContentService.SelectedBrandName + " in " + _scopedContentService.SelectedLanguage + ". Use the following content: `" + string systemMessage = "You are a helpful, " + Mood + " assistant that welcomes the user in the name of the brand or person described by the content, on a website of " + _scopedContentService.SelectedBrandName + " in " + _scopedContentService.SelectedLanguage + ". Use the following content: `" +
extractedText + "` " + extractedText + "` " +
//"and generate a short" +Mood+ "but kind marketing-oriented welcome message and introduction of the brand for the user, constructed as simple Bootstrap HTML codeblock with a <h1> tagged title and a paragraph." + //"and generate a short" +Mood+ "but kind marketing-oriented welcome message and introduction of the brand for the user, constructed as simple Bootstrap HTML codeblock with a <h1> tagged title and a paragraph." +
"and generate a" + Mood + " marketing-oriented welcome message and a summary of the content and introduction of the brand for the user, aiming to explain clearly, what does the company/person offer, constructed as simple Bootstrap HTML codeblock with a <h1> tagged title and a paragraph." + "and generate a" + Mood + " marketing-oriented welcome message and a summary of the content and introduction of the brand for the user, aiming to explain clearly, what does the company/person offer, constructed as simple Bootstrap HTML <div clss=\"container\"> codeblock with a <h1> tagged title and a paragraph." +
"If there is any logo, or not logo but main brand image in the document use that url, and add that as a bootstrap responsive ('img-fluid py-3') image, with the maximum height of 30vh." + "If there is any logo, or not logo but main brand image in the document use that url, and add that as a bootstrap responsive ('img-fluid py-3') image, with the maximum height of 30vh." +
//"In the end of your answer, always add in a new row: 'If you have any questions, you can simply ask either by typing in the message box, or by clicking the microphone icon on the top of the page. '"+ //"In the end of your answer, always add in a new row: 'If you have any questions, you can simply ask either by typing in the message box, or by clicking the microphone icon on the top of the page. '"+
"Here is a list of topics " + menuList + "Here is a list of topics " + menuList +
@ -159,14 +164,14 @@ namespace BLAIzor.Services
} }
//_scopedContentService.CurrentDOM = streamedHtmlContent; //_scopedContentService.CurrentDOM = streamedHtmlContent;
//Console.Write("Answer: " + streamedHtmlContent); ////Console.Write("Answer: " + streamedHtmlContent);
//return streamedHtmlContent; //return streamedHtmlContent;
} }
public async Task ProcessUserIntent(string sessionId, string userPrompt, int siteId, int templateId, string collectionName, string menuList = "") public async Task ProcessUserIntent(string sessionId, string userPrompt, int siteId, int templateId, string collectionName, string menuList = "")
{ {
Console.WriteLine($"SITE ID: {siteId}"); //Console.WriteLine($"SITE ID: {siteId}");
OnStatusChangeReceived?.Invoke(sessionId, "Understanding your request..."); OnStatusChangeReceived?.Invoke(sessionId, "Understanding your request...");
// Get JSON result based on siteId presence // Get JSON result based on siteId presence
@ -174,7 +179,7 @@ namespace BLAIzor.Services
? await GetJsonResultFromQuery(sessionId, siteId, userPrompt) ? await GetJsonResultFromQuery(sessionId, siteId, userPrompt)
: await GetJsonResultFromQuery(sessionId, userPrompt); : await GetJsonResultFromQuery(sessionId, userPrompt);
Console.WriteLine(resultJson); //Console.WriteLine(resultJson);
var baseResult = await ValidateAndFixJson<ChatGPTResultBase>(resultJson, FixJsonWithAI); var baseResult = await ValidateAndFixJson<ChatGPTResultBase>(resultJson, FixJsonWithAI);
@ -183,7 +188,7 @@ namespace BLAIzor.Services
if (baseResult == null) if (baseResult == null)
{ {
Console.WriteLine("Invalid JSON response."); //Console.WriteLine("Invalid JSON response.");
return; return;
} }
@ -209,7 +214,7 @@ namespace BLAIzor.Services
break; break;
default: default:
Console.WriteLine("Unknown result type."); //Console.WriteLine("Unknown result type.");
break; break;
} }
@ -228,13 +233,14 @@ namespace BLAIzor.Services
public async Task ProcessContentRequest(string sessionId, MenuItem requestedMenu, int siteId, int templateId, string collectionName, string menuList = "", bool forceUnmodified = false) public async Task ProcessContentRequest(string sessionId, MenuItem requestedMenu, int siteId, int templateId, string collectionName, string menuList = "", bool forceUnmodified = false)
{ {
Console.Write($"\n\n SessionId: {sessionId}\n\n"); //Console.Write($"\n\n SessionId: {sessionId}\n\n");
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument); //string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
string extractedText = ""; string extractedText = "";
if (requestedMenu != null) { if (requestedMenu != null)
{
string qDrantData = await _qDrantService.GetContentAsync(siteId, requestedMenu.PointId); string qDrantData = await _qDrantService.GetContentAsync(siteId, requestedMenu.PointId);
QDrantGetContentPointResult _selectedPoint = new QDrantGetContentPointResult(); QDrantGetContentPointResult _selectedPoint = new QDrantGetContentPointResult();
@ -258,7 +264,7 @@ namespace BLAIzor.Services
public async Task ProcessContentRequest(string sessionId, string requestedMenu, int siteId, int templateId, string collectionName, string menuList = "", bool forceUnmodified = false) public async Task ProcessContentRequest(string sessionId, string requestedMenu, int siteId, int templateId, string collectionName, string menuList = "", bool forceUnmodified = false)
{ {
Console.Write($"\n\n SessionId: {sessionId}\n\n"); //Console.Write($"\n\n SessionId: {sessionId}\n\n");
//string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument); //string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot/Documents/" + _contentService.SelectedDocument);
string extractedText = ""; string extractedText = "";
@ -308,7 +314,7 @@ namespace BLAIzor.Services
if (fixedResult != null) if (fixedResult != null)
{ {
OnStatusChangeReceived?.Invoke(sessionId, "Initiating the task you requested"); OnStatusChangeReceived?.Invoke(sessionId, "Initiating the task you requested");
await DisplayHtml(sessionId, fixedResult.Text, fixedResult.MethodToCall, fixedResult.Parameter, ""); await DisplayHtml(sessionId, fixedResult.Text, fixedResult.MethodToCall, fixedResult.Parameter);
} }
} }
@ -319,7 +325,7 @@ namespace BLAIzor.Services
if (fixedResult != null) if (fixedResult != null)
{ {
string contentJson = await GetContentFromQuery(sessionId, fixedResult.Text, _workingContent); string contentJson = await GetContentFromQuery(sessionId, fixedResult.Text, _workingContent);
Console.Write("\r \n ProcessTextResult: Content: " + contentJson + "\r \n"); //Console.Write("\r \n ProcessTextResult: Content: " + contentJson + "\r \n");
await ProcessContent(sessionId, contentJson, templateId, collectionName); await ProcessContent(sessionId, contentJson, templateId, collectionName);
} }
} }
@ -335,7 +341,7 @@ namespace BLAIzor.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"❌ JSON parse failed: {ex.Message}"); //Console.WriteLine($"❌ JSON parse failed: {ex.Message}");
var prompt = BuildJsonFixPrompt(json, ex.Message, typeof(T).Name); var prompt = BuildJsonFixPrompt(json, ex.Message, typeof(T).Name);
var fixedJson = await aiFixer(prompt); var fixedJson = await aiFixer(prompt);
@ -349,7 +355,7 @@ namespace BLAIzor.Services
} }
catch (Exception ex2) catch (Exception ex2)
{ {
Console.WriteLine($"❌ AI-fix parse failed: {ex2.Message}"); //Console.WriteLine($"❌ AI-fix parse failed: {ex2.Message}");
return default; return default;
} }
} }
@ -422,15 +428,19 @@ namespace BLAIzor.Services
//contentResult.Photos.Add("Compliment response", "https://www.reactiongifs.com/r/review.gif"); //contentResult.Photos.Add("Compliment response", "https://www.reactiongifs.com/r/review.gif");
//We have the text all available now, let's pass it to the voice generator //We have the text all available now, let's pass it to the voice generator
//TODO modify photos handling, move audio generation to the layout area
OnTextContentAvailable?.Invoke(sessionId, fixedResult.Text); OnTextContentAvailable?.Invoke(sessionId, fixedResult.Text);
List<HtmlSnippet> snippets = await GetSnippetsForDisplay(sessionId, collectionName);
string snippets = await GetSnippetsForDisplay(sessionId, templateId, fixedResult.Text, collectionName); //await DisplayLayoutPlanFromContent(sessionId, fixedResult.Text, snippets, fixedResult.Topics, fixedResult.Photos);
await DisplayHtml(sessionId, fixedResult.Text, "", "", snippets, fixedResult.Topics, fixedResult.Photos); var result = await DisplayLayoutPlanFromContent(sessionId, fixedResult.Text, snippets, fixedResult.Topics, fixedResult.Photos);
if (result == null) result = new LayoutPlan();
await DisplayHtml(sessionId, result, snippets, fixedResult.Topics);
//OnContentReceived?.Invoke(sessionId, result.Blocks.Count.ToString());
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error processing content: {ex.Message}"); //Console.WriteLine($"Error processing content: {ex.Message}");
OnContentReceived?.Invoke(sessionId, ex.Message); OnContentReceived?.Invoke(sessionId, ex.Message);
} }
} }
@ -440,8 +450,8 @@ namespace BLAIzor.Services
var errorResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTErrorResult>(resultJson); var errorResult = System.Text.Json.JsonSerializer.Deserialize<ChatGPTErrorResult>(resultJson);
if (errorResult != null) if (errorResult != null)
{ {
Console.WriteLine($"Error Result: {errorResult.Text}"); //Console.WriteLine($"Error Result: {errorResult.Text}");
await DisplayHtml(sessionId, errorResult.Text, "", "", ""); await DisplayLayoutPlanFromContent(sessionId, errorResult.Text, null, null, null);
} }
} }
@ -463,7 +473,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey(); _apiKey = GetApiKey();
//Console.Write("GetJSONResult called: " +extractedText); ////Console.Write("GetJSONResult called: " +extractedText);
string systemMessage = ""; string systemMessage = "";
if (forceUnmodified) if (forceUnmodified)
{ {
@ -478,7 +488,7 @@ namespace BLAIzor.Services
"Step 1: Start with defining above mentioned key topics of the initial document, and making the list of them. " + "Step 1: Start with defining above mentioned key topics of the initial document, and making the list of them. " +
"Step 2: After that add the above mentioned relevant image urls list." + "Step 2: After that add the above mentioned relevant image urls list." +
"Step 3: " + "Step 3: " +
"- Turn the following content into a nice informative webpage content.\r\n " + "- Turn the following content into a nice informative webpage content (DO NOT REMOVE URLS, PHOTO URLS though).\r\n " +
"- Start with the page title.\r\n" + "- Start with the page title.\r\n" +
"- Structure it nicely without leaving out any information.\r\n " + "- Structure it nicely without leaving out any information.\r\n " +
//"*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" + //"*** CONTENT START *** {" + extractedText + "} *** CONTENT END ***.\r\n" +
@ -549,7 +559,7 @@ namespace BLAIzor.Services
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt); interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
} }
OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now"); OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now");
Console.Write("GetContentFromQuery: Result decision: " + interMediateResult); //Console.Write("GetContentFromQuery: Result decision: " + interMediateResult);
return interMediateResult; return interMediateResult;
} }
@ -565,7 +575,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey(); _apiKey = GetApiKey();
//Console.Write("GetJSONResult called: " +extractedText); ////Console.Write("GetJSONResult called: " +extractedText);
var systemMessage = "You are a helpful assistant. Respond strictly in " + _scopedContentService.SelectedLanguage + " as a JSON object in the following format:\r\n\r\n1. " + var systemMessage = "You are a helpful assistant. Respond strictly in " + _scopedContentService.SelectedLanguage + " as a JSON object in the following format:\r\n\r\n1. " +
//"**chatGPTContentResult**:\r\n " + //"**chatGPTContentResult**:\r\n " +
@ -616,7 +626,7 @@ namespace BLAIzor.Services
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt); interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
} }
OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now"); OnStatusChangeReceived?.Invoke(sessionId, "Mkay, I know now");
Console.Write("GetExaminationResult: Result decision: " + interMediateResult); //Console.Write("GetExaminationResult: Result decision: " + interMediateResult);
return interMediateResult; return interMediateResult;
} }
@ -631,7 +641,7 @@ namespace BLAIzor.Services
_apiKey = GetApiKey(); _apiKey = GetApiKey();
string extractedText = WordFileReader.ExtractText(rootpath); string extractedText = WordFileReader.ExtractText(rootpath);
Console.Write("GetJSONResult called!"); //Console.Write("GetJSONResult called!");
var systemMessage = "You are a helpful assistant. Respond strictly in " + _scopedContentService.SelectedLanguage + " as a JSON object in the following formats:\r\n\r\n1. " + var systemMessage = "You are a helpful assistant. Respond strictly in " + _scopedContentService.SelectedLanguage + " as a JSON object in the following formats:\r\n\r\n1. " +
"1. MethodResult:\r\n " + "1. MethodResult:\r\n " +
@ -685,7 +695,7 @@ namespace BLAIzor.Services
{ {
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt); interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
} }
Console.Write("Result decision: " + interMediateResult); //Console.Write("Result decision: " + interMediateResult);
return interMediateResult; return interMediateResult;
} }
@ -721,11 +731,11 @@ namespace BLAIzor.Services
extractedText = "VECTOR ERROR: ZERO INFORMATION FOUND"; extractedText = "VECTOR ERROR: ZERO INFORMATION FOUND";
} }
_workingContent = extractedText; _workingContent = extractedText.Replace("\"", "'");
Console.Write("\r \n GetJsonResultFromQuery: Working content: " + _workingContent + "\r \n"); //Console.Write("\r \n GetJsonResultFromQuery: Working content: " + _workingContent + "\r \n");
//string extractedText = WordFileReader.ExtractText(rootpath); //string extractedText = WordFileReader.ExtractText(rootpath);
Console.Write("GetJSONResult called!"); //Console.Write("GetJSONResult called!");
string systemMessage = $"You are a helpful assistant built in a website, trying to figure out what the User wants to do or know about.\r\n" + string systemMessage = $"You are a helpful assistant built in a website, trying to figure out what the User wants to do or know about.\r\n" +
"Your job is to classify the user's request into one of the following categories:\r\n" + "Your job is to classify the user's request into one of the following categories:\r\n" +
@ -801,133 +811,148 @@ namespace BLAIzor.Services
interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt); interMediateResult = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userPrompt);
} }
Console.Write("\r \n GetJsonResultFromQuery: Result decision: " + interMediateResult + "\r \n"); //Console.Write("\r \n GetJsonResultFromQuery: Result decision: " + interMediateResult + "\r \n");
return interMediateResult; return interMediateResult;
} }
public async Task<string> GetSnippetsForDisplay(string sessionId, int templateId, string interMediateResult, string collectionName) public async Task<List<HtmlSnippet>> GetSnippetsForDisplay(string sessionId, string collectionName)
{ {
_apiKey = GetApiKey(); _apiKey = GetApiKey();
OnStatusChangeReceived?.Invoke(sessionId, "Looking up the UI template elements for you"); OnStatusChangeReceived?.Invoke(sessionId, "Looking up the UI template elements for you");
string availableSnippetList = ""; //string availableSnippetList = "";
List<HtmlSnippet> snippets = new List<HtmlSnippet>();
var snippetscount = await _qDrantService.GetCollectionCount(collectionName); var snippetscount = await _qDrantService.GetCollectionCount(collectionName);
for (int j = 1; j <= snippetscount; j++) for (int j = 1; j <= snippetscount; j++)
{ {
var snippet = await _qDrantService.GetSnippetAsync(j, collectionName); var snippet = await _qDrantService.GetSnippetAsync(j, collectionName);
QDrantGetPointResult x = JsonConvert.DeserializeObject<QDrantGetPointResult>(snippet); QDrantGetPointResult x = JsonConvert.DeserializeObject<QDrantGetPointResult>(snippet);
snippets.Add(new HtmlSnippet { Id = x.result.payload.Id,
availableSnippetList += ("- " + x.result.payload.Name + ": " + x.result.payload.Description + ".\r\n"); Name = x.result.payload.Name,
Description = x.result.payload.Description,
Type = x.result.payload.Type,
Variant = x.result.payload.Variant,
Tags = x.result.payload.Tags,
Slots = x.result.payload.Slots,
Html = x.result.payload.Html,
SampleHtml = x.result.payload.SampleHtml
});
//availableSnippetList += ("- " + x.result.payload.Name + ": " + x.result.payload.Description + ".\r\n");
} }
Console.Write(availableSnippetList); ////Console.Write(availableSnippetList);
OnStatusChangeReceived?.Invoke(sessionId, "Fitting content to available UI elements"); OnStatusChangeReceived?.Invoke(sessionId, "Loading UI elements from the design database");
var systemMessage = "You are a helpful assistant for generating responses using HTML templates. Analyze the user query and choose the most suitable snippet type(s) for rendering the response." +
"Respond with only the snippet name(s), comma-separated. No explanation. Identify the most suitable " +
"HTML code snippet type to use for rendering your response based on user queries. The available snippet types are: " +
availableSnippetList +
//"The content to answer from from is here: " + extractedText + ". " +
"If multiple snippets apply, list them in order of priority. ";
var userMessage = "How would you render the following text in html: " + interMediateResult + " ? "; //WTF????????
string result = string.Empty;
if (!UseWebsocket)
{
AiProvider = GetAiSettings(); //var systemMessage = "You are a helpful assistant for generating responses using HTML templates. Analyze the user query and choose the most suitable snippet type(s) for rendering the response." +
if (AiProvider == "cerebras") // "Respond with only the snippet name(s), comma-separated. No explanation. Identify the most suitable " +
{ // "HTML code snippet type to use for rendering your response based on user queries. The available snippet types are: " +
result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userMessage); // availableSnippetList +
} // //"The content to answer from from is here: " + extractedText + ". " +
else if (AiProvider == "chatgpt") // "If multiple snippets apply, list them in order of priority. ";
{
result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
} //var userMessage = "How would you render the following text in html: " + interMediateResult + " ? ";
else if (AiProvider == "deepseek")
{
result = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
}
} //string result = string.Empty;
else //if (!UseWebsocket)
{ //{
result = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userMessage);
}
//Console.Write(result); // AiProvider = GetAiSettings();
List<string> snippetList = result.Split(new[] { ',' }).ToList(); // if (AiProvider == "cerebras")
// {
// result = await _cerebrasAPIService.GetSimpleCerebrasResponse(sessionId, systemMessage, userMessage);
// }
// else if (AiProvider == "chatgpt")
// {
// result = await _openAIApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
int i = 0; // }
// else if (AiProvider == "deepseek")
// {
// result = await _deepSeekApiService.GetSimpleChatGPTResponse(sessionId, systemMessage, userMessage);
// }
OnStatusChangeReceived?.Invoke(sessionId, "Oooh got it! Loading UI elements from the design database"); //}
//Console.Write("ChatGPT decided!!!! "); //else
//{
// result = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userMessage);
//}
//////Console.Write(result);
//List<string> snippetList = result.Split(new[] { ',' }).ToList();
//int i = 0;
//OnStatusChangeReceived?.Invoke(sessionId, "Oooh got it! Loading UI elements from the design database");
////Console.Write("ChatGPT decided!!!! ");
//let's send result to embeddingservice //let's send result to embeddingservice
List<float[]> vectorsList = new List<float[]>(); //List<float[]> vectorsList = new List<float[]>();
foreach (var snippet in snippetList) //foreach (var snippet in snippetList)
{ //{
var vectors = await _openAIEmbeddingService.GenerateEmbeddingAsync(result); // var vectors = await _openAIEmbeddingService.GenerateEmbeddingAsync(result);
vectorsList.Add(vectors); // vectorsList.Add(vectors);
} //}
List<int> pointIds = new List<int>(); //List<int> pointIds = new List<int>();
var collectionCount = await _qDrantService.GetCollectionCount(collectionName); //var collectionCount = await _qDrantService.GetCollectionCount(collectionName);
if (collectionCount > 0) //if (collectionCount > 0)
{ //{
foreach (var vector in vectorsList) // foreach (var vector in vectorsList)
{ // {
var qDrantresult = await _qDrantService.QuerySnippetAsync(vector, 3, collectionName); // var qDrantresult = await _qDrantService.QuerySnippetAsync(vector, 3, collectionName);
pointIds.Add(qDrantresult); // pointIds.Add(qDrantresult);
} // }
List<string> qDrantDataList = new List<string>(); // List<string> qDrantDataList = new List<string>();
foreach (var pointId in pointIds) // foreach (var pointId in pointIds)
{ // {
var qDrantData = await _qDrantService.GetSnippetAsync(pointId, collectionName); // var qDrantData = await _qDrantService.GetSnippetAsync(pointId, collectionName);
qDrantDataList.Add(qDrantData); // qDrantDataList.Add(qDrantData);
} // }
List<QDrantGetPointResult> selectedPointList = new List<QDrantGetPointResult>(); // List<QDrantGetPointResult> selectedPointList = new List<QDrantGetPointResult>();
if (qDrantDataList != null) // if (qDrantDataList != null)
{ // {
foreach (var x in qDrantDataList) // foreach (var x in qDrantDataList)
{ // {
var selectedPoint = JsonConvert.DeserializeObject<QDrantGetPointResult>(x)!; // var selectedPoint = JsonConvert.DeserializeObject<QDrantGetPointResult>(x)!;
selectedPointList.Add(selectedPoint); // selectedPointList.Add(selectedPoint);
} // }
} // }
string htmlToUse = "Your snippets to use: "; // string htmlToUse = "Your snippets to use: ";
foreach (var selectedPoint in selectedPointList) // foreach (var selectedPoint in selectedPointList)
{ // {
htmlToUse += selectedPoint.result.payload.Name + ": " + selectedPoint.result.payload.Html + ", "; // htmlToUse += selectedPoint.result.payload.Name + ": " + selectedPoint.result.payload.Html + ", ";
} // }
//Console.Write(htmlToUse); // ////Console.Write(htmlToUse);
return htmlToUse; // return htmlToUse;
} //}
else return "No snippets found, use bootstrap html components"; return snippets;
} }
public async Task DisplayHtml(string sessionId, string interMediateResult, string methodToCall = "", string methodParameter = "", string htmlToUse = "", string[]? topics = null, Dictionary<string, string>? photos = null) public async Task DisplayHtml(string sessionId, LayoutPlan layoutPlan, List<HtmlSnippet> htmlToUse, string[]? topics = null)
{ {
Console.Write($"\n SessionId: {sessionId} \n"); //for textresult and errorresult
//Console.Write($"\n SessionId: {sessionId} \n");
OnStatusChangeReceived?.Invoke(sessionId, "Casting spells to draw customized UI"); OnStatusChangeReceived?.Invoke(sessionId, "Casting spells to draw customized UI");
Console.WriteLine($"DISPLAYHTML Text: {interMediateResult}\n\n"); //Console.WriteLine($"DISPLAYHTML Snippets: {htmlToUse.Count}\n\n");
Console.WriteLine($"DISPLAYHTML Snippets: {htmlToUse}\n\n"); //Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
Console.WriteLine($"DISPLAYHTML Photos: {photos} \n\n");
Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
StringBuilder lst = new StringBuilder("You are a helpful assistant generating HTML content in " + _scopedContentService.SelectedLanguage + " using Bootstrap. \n\n" + StringBuilder lst = new StringBuilder("You are a helpful assistant generating HTML content in " + _scopedContentService.SelectedLanguage + " using Bootstrap. \n\n" +
"### Rules to Follow: \n" + "### Rules to Follow: \n" +
"- Please generate clean and structured HTML that fits inside a Bootstrap container.\n" + "- Please generate clean and structured HTML that goes between the menu and the footer of a Bootstrap5 webpage.\n" +
"- DO NOT include `<head>`, `<body>`, or `<script>` tags—only content inside the Bootstrap container.\n" + "- DO NOT include `<head>`, `<body>`, or `<script>` tags—only content inside the Bootstrap container.\n" +
"- Use `<h1 class='p-3'>` for the title, based on the user's query.\n" + "- Use `<h1 class='p-3'>` for the title, based on the user's query.\n" +
"- Structure content using **separate `row` elements** for different sections.\n" + "- Structure content using **separate `row` elements** for different sections.\n" +
@ -938,51 +963,53 @@ namespace BLAIzor.Services
"- Do NOT nest images inside paragraphs! \n" + "- Do NOT nest images inside paragraphs! \n" +
"- Ensure clear **separation of content into multiple sections if multiple snippets are provided**.\n"); "- Ensure clear **separation of content into multiple sections if multiple snippets are provided**.\n");
if (!string.IsNullOrEmpty(methodToCall))
{ if (htmlToUse != null)
lst.AppendLine("- At the END of the content, include a **single** Bootstrap button (`btn btn-primary`) that calls `" + methodToCall + "` with `" + methodParameter + "` on click.\n");
}
else
{
if (!string.IsNullOrEmpty(htmlToUse))
{ {
lst.AppendLine( lst.AppendLine(
"### Using Provided Snippets:\n" + "### Using Provided Snippets:\n" +
"- You have been given **multiple HTML snippets**:\n \n " + htmlToUse + ". \n \n **DO NOT merge them into one**.\n" + "- You have been given **multiple HTML snippets**:\n \n");
"- Use each snippet **as a separate section** inside its own `div.row`.\n" + foreach (var snippet in htmlToUse)
"- Using the snippets is NOT mandatory, use them only if you identified content that fits in a specific snippet.\n" + {
lst.AppendLine($"{snippet.Id}: {snippet.Name}: {snippet.Html}. \n \n");
lst.AppendLine($"Type: {snippet.Type}, Tags: {snippet.Tags}, Variant: {snippet.Variant}. \n \n");
}
lst.AppendLine("**DO NOT merge them into one**.\n" +
"- Use each snippet **as a separate section** inside its own div with class:`container-fluid`.\n" +
"- Using all the snippets is NOT mandatory, use them only if you identified content that fits in a specific snippet.\n" +
"- Validate if there are multiple variants of a snippet, and choose the best option, for example if the block has text and photo url, choose the variant that has image in it.\n \n" +
//"- Maintain their order and **DO NOT omit any snippet**.\n" + //"- Maintain their order and **DO NOT omit any snippet**.\n" +
"- Example structure:\n\n" + //"- Example structure:\n\n" +
" ```html\n" + //" ```html\n" +
" <div class='container'>\n" + //" <div class='container-fluid'>\n" +
" <div class='row'>\n" + //" <div class='row'>\n" +
" <!-- First snippet -->\n" + //" <!-- First snippet -->\n" +
" </div>\n" + //" </div>\n" +
" <div class='row'>\n" + //" <div class='row'>\n" +
" <!-- Second snippet -->\n" + //" <!-- Second snippet -->\n" +
" </div>\n" + //" </div>\n" +
" </div>\n" + //" </div>\n" +
" ```\n\n" + //" ```\n\n" +
"- If a snippet contains a button, ensure it is placed inside a `div.text-center` for proper alignment.\n"); "- If a snippet contains a button, ensure it is placed inside a `div.text-center` for proper alignment.\n");
} }
if (photos != null && photos.Any()) //if (photos != null && photos.Any())
{ //{
lst.AppendLine( // lst.AppendLine(
"### Handling Photos:\n" + // "### Handling Photos:\n" +
"- ONLY use the provided photo URLs **as they are**, without modification.\n" + // "- ONLY use the provided photo URLs **as they are**, without modification.\n" +
"- Do **NOT** generate or assume image URLs.\n" + // "- Do **NOT** generate or assume image URLs.\n" +
"- Use these photos where appropriate:\n" + // "- Use these photos where appropriate:\n" +
" " + string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")) + "\n\n" + // " " + string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")) + "\n\n" +
"- Example usage:\n" + // "- Example usage:\n" +
//" ```html\n" + // //" ```html\n" +
" <img src='" + photos.First().Value + "' class='img-fluid' alt='" + photos.First().Key + "' />\n" + // " <img src='" + photos.First().Value + "' class='img-fluid' alt='" + photos.First().Key + "' />\n" +
//" ```\n\n" + // //" ```\n\n" +
"DO NOT modifiy the photo urls in any way." // "DO NOT modifiy the photo urls in any way."
); // );
} //}
if (topics != null && topics.Any()) if (topics != null && topics.Any())
{ {
@ -1004,13 +1031,316 @@ namespace BLAIzor.Services
{ {
lst.AppendLine("- **No topics provided** → **Do NOT generate topic buttons.**"); lst.AppendLine("- **No topics provided** → **Do NOT generate topic buttons.**");
} }
lst.AppendLine(
"- DO **NOT** merge different content sections.\n" +
"- DO **NOT** wrap the entire content in a single `div`—use separate `container-fluid` divs.\n" +
"- DO **NOT** modify the photo urls in any way." +
"- Do **NOT** generate or assume new photo URLs.\n" +
"- Do **NOT** skip or deny ANY part of the provided text. All of the provided text should be displayed as html\n" +
"- Do **NOT** assume ANY content, like missing prices, missing links. \n" +
"- If the snippet contains an image, but there is no photo url available, SKIP adding the image tag." +
"- **Never** add explanations or start with ```html syntax markers.\n");
string systemMessage = lst.ToString();
string userMessage = "Create a perfect, production ready, structured, responsive Bootstrap 5 webpage " +
"content from the provided layout and available html snippets. \r \n" +
"Read the layout plan, analyze the blocks, and identify if there is any matching html snippets for each block folowing these rules: \r \n" +
"- If there is no matching html snippet, generate the bootstrap 5 based layout for the given block. Else parse the block content into the available code snippet." +
"- For each block, add am opening and closing comment with the name of the block like: <!-- Hero start --> \r \n" +
"If the block's rawcontent contains photo url, display that photo within that section, not elsewhere.";
string assistantMessage = "`Provided layout plan, that contains the text to be displayed as HTML`:";
foreach (var block in layoutPlan.Blocks)
{
assistantMessage += $"Block type: {block.Type}, block content: {block.RawContent}";
} }
//int mode = -1;
if (!UseWebsocket)
{
AiProvider = GetAiSettings();
if (AiProvider == "cerebras")
{
await _cerebrasAPIService.GetCerebrasStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else if (AiProvider == "chatgpt")
{
await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else if (AiProvider == "deepseek")
{
//await _deepSeekApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
//await _openAIApiService.GetChatGPTStreamedResponse(sessionId, systemMessage, userMessage, assistantMessage, -1);
}
else
{
await _openAIRealtimeService.GetChatGPTResponseAsync(sessionId, systemMessage, userMessage + assistantMessage);
}
}
public async Task<LayoutPlan?> DisplayLayoutPlanFromContent(string sessionId, string interMediateResult, List<HtmlSnippet> htmlToUse, string[]? topics = null, Dictionary<string, string>? photos = null)
{
//Console.Write($"\n SessionId: {sessionId} \n");
OnStatusChangeReceived?.Invoke(sessionId, "Planning layout based on the provided content");
//Console.WriteLine($"LAYOUTBUILDER Text: {interMediateResult}\n\n");
//Console.WriteLine($"LAYOUTBUILDER Snippets: {htmlToUse.Count}\n\n");
//Console.WriteLine($"LAYOUTBUILDER Photos: {photos} \n\n");
//Console.WriteLine($"LAYOUTBUILDER Topics: {topics} \n\n");
var sb = new StringBuilder();
// 📌 STRICT FORMAT INSTRUCTION
sb.AppendLine("You are a helpful assistant whose ONLY task is to break down THE PROVIDED CONTENT into a structured JSON object for Bootstrap 5 pages, using .\n" +
"**layoutplan**:\r\n" +
"- `title`: \"The title of the page\"\r\n" +
"- `blocks`: \"an array of layoutblocks\"\r\n" +
".");
if ((htmlToUse != null))
{
sb.AppendLine("\n### Snippets Provided:");
foreach (var snippet in htmlToUse)
{
sb.AppendLine($"Snippet id: {snippet.Id}, Snippet name: {snippet.Name}, Snippet description: {snippet.Description}, Snippet type: {snippet.Type}, Snippet template code: {snippet.Html}");
}
sb.AppendLine("- Use them ONLY if relevant.");
}
if (photos != null && photos.Any())
{
sb.AppendLine("\n### Photos to be used in the blocks:");
sb.AppendLine(string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}")));
}
if (topics != null && topics.Any())
{
sb.AppendLine("\n### Topics:");
sb.AppendLine(string.Join(", ", topics));
}
sb.AppendLine("### ⛔️ ABSOLUTELY DO NOT:");
sb.AppendLine("- ❌ Return HTML structure trees like `<section><div>`...");
sb.AppendLine("- ❌ Include keys like `src`, `alt`, `url`, `items`, or nested object lists.");
sb.AppendLine("- ❌ Invent block types not on the list.");
sb.AppendLine("- ❌ Return any explanation, markdown, prose, comments, or fallback formats.");
sb.AppendLine("- ❌ Use any of these block types: `image`, `quote`, `list`, `layout`, `header`, `footer`, `sidebar`, etc.");
sb.AppendLine("\n### ✅ ALLOWED OUTPUT FORMAT:");
sb.AppendLine("{");
sb.AppendLine(" \"title\": \"string\",");
sb.AppendLine(" \"blocks\": [");
sb.AppendLine(" { \"type\": \"string\", \"rawcontent\": \"string (text)\", \"preferredsnippetid\" : an integer id of matching html snippet if one found., \"order\": an incremented integer to set the blocks in order }");
sb.AppendLine(" ]");
sb.AppendLine("}");
sb.AppendLine("\n### ✅ Allowed `type` values (only use these):");
sb.AppendLine("- hero");
sb.AppendLine("- text");
sb.AppendLine("- text-image");
sb.AppendLine("- features");
sb.AppendLine("- cta");
sb.AppendLine("- video");
sb.AppendLine("- icon-list");
sb.AppendLine("- event-list");
sb.AppendLine("- audio-player");
sb.AppendLine("- topic-buttons");
sb.AppendLine("\n### ❗ GOOD EXAMPLE:");
sb.AppendLine("{");
sb.AppendLine(" \"title\": \"Welcome to Our Product\",");
sb.AppendLine(" \"blocks\": [");
sb.AppendLine(" { \"type\": \"hero\", \"rawcontent\": \"Example text\", \"preferredsnippetid\": 1, \"order\": 0 },");
sb.AppendLine(" { \"type\": \"features\", \"rawcontent\": \"Example features\" }, \"order\": 1 },");
sb.AppendLine(" { \"type\": \"cta\", \"rawcontent\": \"call to action content\", \"order\": 2 } }");
sb.AppendLine(" ]");
sb.AppendLine("}");
sb.AppendLine("\n### ⛔ BAD EXAMPLES (DO NOT DO THIS):");
sb.AppendLine("- { \"type\": \"image\", \"src\": \"...\", \"alt\": \"...\" }");
sb.AppendLine("- { \"type\": \"list\", \"items\": [\"...\", \"...\"] }");
sb.AppendLine("- { \"layout\": { \"header\": ..., \"footer\": ... } }");
sb.AppendLine("\n### FINAL TASK:");
sb.AppendLine("Based on the following content, generate a JSON object following the exact format above. Output only the JSON. No markdown. No explanation.");
//sb.AppendLine("\nCONTENT TO ANALYZE FOR STRUCTURING:");
//sb.AppendLine(interMediateResult);
string systemMessage = sb.ToString();
string userMessage = "Based on the following content:\n\n*** " + interMediateResult + " ***\n\n" +
"and the available photos:";
if (photos != null && photos.Any())
{
userMessage += "\n### Photo urls to be used WITHOUT ANY MODIFICATION in the blocks:";
userMessage += string.Join(", ", photos.Select(kv => $"{kv.Key}: {kv.Value}"));
}
userMessage += ", return ONLY a JSON object representing a structured content layout with this exact shape:\n" +
"{ \"title\": string, \"blocks\": [ { \"type\": \"string\", \"rawcontent\": \"string (text)\", \"preferredsnippetid\" : an integer id of matching html snippet if one found., \"order\": an incremented integer to set the blocks in order } ] }\n\n" +
"⚠️ DO NOT use layout structures like 'header', 'sidebar', 'mainContent', or 'footer'.\n" +
"⚠️ DO NOT explain anything. Just return the pure JSON. No markdown, no ``` markers.\n\n" +
"🎯 OBJECTIVE:\n" +
"1. Determine 1 to 5 major content blocks from the input, grouping full paragraphs or sections together.\n" +
"Prefer **1 to 5 blocks max**, unless the content is extremely long. Each block should represent a semantically complete section.\r\n" +
"Group related ideas into one block, even if they are multiple paragraphs. Do NOT break up content just because it is a new sentence. \r\n" +
"2. Use meaningful, semantically grouped layout block types like: 'hero', 'features list', 'text with image', 'text', 'product list', 'call to action', 'videoplayer', 'audioplayer', etc.\n" +
"3. Each block should contain **rich content**, not just one or two sentences. Group related ideas into a single `rawcontent` field.\n\n" +
"DO NOT REMOVE ANY URLS (photo, wen link, etc) from the section or paragraph that it follows. \n\n" +
//"4. For ALL available image urls in the text, find a relevant block, and attach the UNMODIFIED photo url, to the specific block's rawcontent, like \"Photo url\": \"the provided photo url\".\n\n" +
"📌 Examples of good block grouping:\n" +
"- All introductory marketing text together in one 'hero' or 'text' block\n" +
"- A group of benefits or features into a single 'features' block\n" +
"- A pitch paragraph into a 'call to action' block\n\n" +
"🎨 Naming:\n" +
"- Try to find related type values in the available snippets list. If you find a relevant snippet for that block, use the name of that snippet for type, and add the id if the snippet as preferredsnippetid." +
"- If there is no relevant snippet, use such layout `type` values: hero, text, features, text with image, call to action, product list, team members, testimonial, event list, blogpost, article, video player, audio player, etc\n" +
//"- Use lowercase strings only.\n\n" +
"X Restrictions:"+
"- DO **NOT** modify the photo urls in any way." +
"- Do **NOT** generate or assume new photo URLs.\n" +
"- Do **NOT** modify photo URLs in any way.\n" +
"- Do **NOT** skip or deny ANY part of the provided text. All of the provided text should be displayed as html \n" +
"- Do **NOT** assume ANY content, like missing prices, missing links, etc. \n"+
"✅ Final output: JSON only, well grouped, no extra explanation or formatting, no markdown, no ``` markers.\n";
string assistantMessage = "";
//Console.WriteLine($"LAYOUTBUILDER PROMPT: {systemMessage}\n\n");
// 🛡 Retry logic
string aiResponse = string.Empty;
LayoutPlan? layoutPlan = null;
int retry = 0;
AiProvider = GetAiSettings();
while (layoutPlan == null && retry < 2)
{
if (AiProvider == "chatgpt")
{
aiResponse = await _openAIApiService.GetSimpleChatGPTResponse(systemMessage, userMessage, assistantMessage);
}
else if (AiProvider == "cerebras")
{
aiResponse = await _cerebrasAPIService.GetSimpleCerebrasResponse(systemMessage, userMessage, assistantMessage);
}
else if (AiProvider == "deepseek")
{
aiResponse = await _deepSeekApiService.GetSimpleChatGPTResponse(systemMessage, userMessage, assistantMessage);
}
else
{
aiResponse = await _openAIRealtimeService.GetFullChatGPTResponseAsync(sessionId, systemMessage, userMessage, null);
}
if (string.IsNullOrWhiteSpace(aiResponse))
{
//Console.WriteLine("Empty response from AI.");
return null;
}
try
{
//Console.WriteLine("AI Response:");
//Console.WriteLine(aiResponse);
aiResponse = FixJsonWithoutAI(aiResponse);
layoutPlan = System.Text.Json.JsonSerializer.Deserialize<LayoutPlan>(aiResponse, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
if (layoutPlan?.Blocks == null || layoutPlan.Blocks.Any(b => string.IsNullOrEmpty(b.Type) || string.IsNullOrEmpty(b.RawContent)))
{
//try to fix with AI)
//Console.WriteLine("Trying to fix with AI.");
layoutPlan = await ValidateAndFixJson<LayoutPlan>(aiResponse, FixJsonWithAI);
if (layoutPlan?.Blocks == null || layoutPlan.Blocks.Any(b => string.IsNullOrEmpty(b.Type) || string.IsNullOrEmpty(b.RawContent)))
{
Console.WriteLine("Invalid block structure in response.");
layoutPlan = null;
}
}
}
catch (Exception ex)
{
Console.WriteLine("Deserialization failed: " + ex.Message);
Console.WriteLine("Deserialization failed on json: " + aiResponse);
layoutPlan = null;
}
if (layoutPlan == null)
{
retry++;
Console.WriteLine("Retrying due to invalid format...");
}
}
return layoutPlan;
}
private string FixJsonWithoutAI(string aiResponse)
{
if (aiResponse.StartsWith("```"))
{
//Console.WriteLine("FIXING ``` in AI Response.");
aiResponse = aiResponse.Substring(3);
if (aiResponse.StartsWith("json"))
{
aiResponse = aiResponse.Substring(4);
}
aiResponse = aiResponse.Substring(0, aiResponse.Length - 3);
}
return aiResponse;
}
public async Task DisplayHtml(string sessionId, string interMediateResult, string methodToCall = "", string methodParameter = "")//, string[]? topics = null)
{
//for methodResult
//Console.Write($"\n SessionId: {sessionId} \n");
OnStatusChangeReceived?.Invoke(sessionId, "Casting spells to draw customized UI");
////Console.WriteLine($"DISPLAYHTML Text: {interMediateResult}\n\n");
////Console.WriteLine($"DISPLAYHTML Topics: {topics} \n\n");
StringBuilder lst = new StringBuilder("You are a helpful assistant generating HTML content in " + _scopedContentService.SelectedLanguage + " using Bootstrap. \n\n" +
"### Rules to Follow: \n" +
"- Please generate clean and structured HTML that fits inside a Bootstrap container.\n" +
"- DO NOT include `<head>`, `<body>`, or `<script>` tags—only content inside the Bootstrap container.\n" +
"- Use `<h1 class='p-3'>` for the title, based on the user's query.\n" +
"- Structure content using **separate `row` elements** for different sections.\n" +
"- Use Bootstrap classes to ensure proper spacing and alignment.\n" +
" - Use 'row justify-content-center' for layout, 'col-xx-x' classes for columns (ONLY IF multiple columns are needed), and always use 'img-fluid' class for images. \r\n" +
" - Do NOT use and 'col' class if there is only one column to display in the row. \n" +
"- Do NOT use additional class for paragraphs! \n" +
"- Do NOT nest images inside paragraphs! \n" +
"- Ensure clear **separation of content into multiple sections if multiple snippets are provided**.\n");
if (!string.IsNullOrEmpty(methodToCall))
{
lst.AppendLine("- At the END of the content, include a **single** Bootstrap button (`btn btn-primary`) that calls `" + methodToCall + "` with `" + methodParameter + "` on click.\n");
}
lst.AppendLine( lst.AppendLine(
"- DO **NOT** merge different content sections.\n" + "- DO **NOT** merge different content sections.\n" +
"- DO **NOT** wrap the entire content in a single `div`—use separate `row` divs.\n" + "- DO **NOT** wrap the entire content in a single `div`—use separate `row` divs.\n" +
"- DO **NOT** modify the photo urls in any way." +
"- Do **NOT** generate or assume new photo URLs.\n" +
"- Do **NOT** skip or deny ANY part of the provided text. All of the provided text should be displayed as html\n" + "- Do **NOT** skip or deny ANY part of the provided text. All of the provided text should be displayed as html\n" +
"- Do **NOT** assume ANY content, like missing prices, missing links. \n" + "- Do **NOT** assume ANY content, like missing prices, missing links. \n" +
"- If the snippet contains an image, but there is no photo url available, SKIP adding the image tag." + "- If the snippet contains an image, but there is no photo url available, SKIP adding the image tag." +

View File

@ -1,6 +1,14 @@
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap'); /*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/ @import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/
.rz-panel {
--rz-panel-padding: 0.6rem !important;
}
.rz-accordion .rz-accordion-header > a {
--rz-accordion-item-padding-block: 0.6rem !important;
}
._poweredBy_1f9vw_251 { ._poweredBy_1f9vw_251 {
display: none !important; display: none !important;
} }
@ -261,3 +269,83 @@ ul {
.rz-html-editor { .rz-html-editor {
background-color: transparent !important; background-color: transparent !important;
} }
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
/* Mobile Responsiveness */
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.6rem;
}
h3 {
font-size: 1.4rem;
}
p, li {
font-size: 1rem;
}
.navbar-collapse .nav-link {
font-size: 1.1rem;
}
p {
text-align: justify;
}
}
@media (max-width: 480px) {
h1 {
font-size: 1.6rem;
}
h2 {
font-size: 1.3rem;
}
h3 {
font-size: 1.1rem;
}
p, li {
font-size: 0.95rem;
}
.navbar-collapse .nav-link {
font-size: 1rem;
}
p {
text-align: justify;
}
}

View File

@ -0,0 +1,168 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -0,0 +1,168 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -0,0 +1,168 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -0,0 +1,168 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -3,11 +3,6 @@
/*search*/ /*search*/
p {
font-size: x-large;
}
li { li {
list-style: none; list-style: none;
} }
@ -20,7 +15,14 @@ label {
.img-fluid { .img-fluid {
max-height: 50vh; max-height: 50vh;
width: auto; width: auto;
border-radius: 5px;
}
.pop-img {
border-radius: 10px !important;
border-color: white;
border-width: 1px;
border-style: solid;
} }
.card-img-top { .card-img-top {
@ -500,3 +502,35 @@ body {
.row { .row {
margin-bottom: 10px; margin-bottom: 10px;
} }
h1 {
color: #64CCC5;
}
p {
color: #EEEEEE;
font-size: 1,1rem;
}
.container-fluid {
margin-bottom:20px;
padding: 20px;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -0,0 +1,536 @@
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/
/*search*/
li {
list-style: none;
}
label {
color: #000;
/* display: none; */
}
.img-fluid {
max-height: 50vh;
width: auto;
}
.pop-img {
border-radius: 10px !important;
border-color: white;
border-width: 1px;
border-style: solid;
}
.card-img-top {
max-height: 50vh;
width: auto;
border-radius: 5px;
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
width: fit-content;
font-weight: bold;
transition: all 0.2s ease;
margin: 15px;
}
.voicebutton {
border-radius: 50% !important;
padding: 10px !important;
width: 40px;
height:40px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 20px;
height: 40px;
transition: all 0.2s ease;
padding: 10px;
margin: 10px;
}
.btn:active {
background: rgba(255, 255, 255, 1);
}
.btn:hover {
background: rgba(255, 255, 255, 0.8);
}
img {
border-radius: 20px !important;
}
input.search_bar{
border: none;
outline: none;
width: 75px;
border-radius: 55px;
margin: 0 auto;
font-size: 1.3em;
color: #0d2840;
padding: 15px 30px 15px 45px;
transition: all .3s cubic-bezier(0,0,.5,1.5);
box-shadow: 0 3px 10px -2px rgba(0,0,0,.1);
background: rgba(255, 255, 255, 0.3) url(https://i.imgur.com/seveWIw.png) no-repeat center center;
}
input.search_bar:focus{
width: 100%;
background-position: calc(100% - 35px) center
}
/*Removes default x in search fields (webkit only i guess)*/
input[type=search]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
/*Changes the color of the placeholder*/
::-webkit-input-placeholder {
color: #0d2840;
opacity: .5;
}
:-moz-placeholder {
color: #0d2840;
opacity: .5;
}
::-moz-placeholder {
color: #0d2840;
opacity: .5;
}
:-ms-input-placeholder {
color: #0d2840;
opacity: .5;
}
/*search*/
/*Search2*/
.searchBox {
width: 60px;
background: rgba(255, 255, 255, 0.3);
height: 60px;
border-radius: 40px;
padding: 10px;
margin: 0 auto;
transition: 0.8s;
}
.searchInput:active > .searchBox{
width:100%
}
.searchInput:focus > .searchBox {
width: 100%
}
.searchInput::placeholder {
color:#fff;
}
.searchBox:hover {
width: 100%;
}
.searchBox:hover > .searchInput {
width: calc(100% - 60px);
padding: 0 6px;
}
.searchBox:hover > .searchButton {
background: white;
color: #2f3640;
}
.searchButton {
color: white;
float: right;
width: 40px;
height: 40px;
border-radius: 50px;
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #0d2840 !important;
float: left;
padding: 0;
color: white;
font-size: 16px;
transition: 0.4s;
line-height: 40px;
width: 0px;
}
/*Search2*/
.event {
border-radius: 20px !important;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
/*card design*/
.card {
border-radius: 20px !important;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
.card:hover {
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1), 0 10px 8px rgba(0, 0, 0, 0.015);
}
.card-body .card-title {
font-family: 'Lato', sans-serif;
font-weight: 700;
letter-spacing: 0.3px;
font-size: 24px;
color: #121212;
}
.card-text {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-size: 15px;
letter-spacing: 0.3px;
color: #4E4E4E;
}
.card .container {
width: 88%;
/*background: #F0EEF8;*/
border-radius: 30px;
/*height: 140px;*/
display: flex;
align-items: center;
justify-content: center;
}
.container:hover > img {
transform: scale(1.2);
}
.container img {
/*padding: 75px;*/
/*margin-top: -40px;
margin-bottom: -40px;*/
transition: 0.4s ease;
cursor: pointer;
}
.btn:hover {
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
}
.btn:focus {
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
}
/*card design*/
/*bg*/
:root {
font-size: 15px;
}
body {
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
/*font-family: 'Quicksand', sans-serif;*/
color: #fff !important;
margin: 0;
min-height: 100vh;
background-color: #000;
/*background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));*/
/*background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;*/
/*background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;*/
background-repeat: no-repeat;
/*animation: 10s movement linear infinite;*/
}
body::after {
content: '';
display: block;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.myspan {
position: relative;
z-index: 10;
display: flex;
min-height: 100vh;
width: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
color: transparent;
text-shadow: 0px 0px 1px rgba(255, 255, 255, .6), 0px 4px 4px rgba(0, 0, 0, .05);
letter-spacing: .2rem;
}
@keyframes movement {
0%, 100% {
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
}
25% {
background-size: 100vmax 100vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 60vmax 60vmax;
background-position: -60vmax -90vmax, 50vmax -40vmax, 0vmax -20vmax, -40vmax -20vmax, 40vmax 60vmax;
}
50% {
background-size: 80vmax 80vmax, 110vmax 110vmax, 80vmax 80vmax, 60vmax 60vmax, 80vmax 80vmax;
background-position: -50vmax -70vmax, 40vmax -30vmax, 10vmax 0vmax, 20vmax 10vmax, 30vmax 70vmax;
}
75% {
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
}
}
/*bg*/
.mytextarea {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(20px);
padding: 10px;
border-radius: 10px;
border-width: 0px;
height: unset !important;
}
.mytextarea:active {
border-width: 0px;
}
.mytextarea:focus-visible {
background-color: rgba(255, 255, 255, 0.5);
border-width: 0px !important;
outline: -webkit-focus-ring-color auto 0px;
outline-color: transparent;
}
.navbar-toggler {
color: #fff;
}
.navbar-brand {
font-size: 1.7rem;
color:#fff;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.form-select > option {
background-color: rgba(255, 255, 255, 0.2)
}
.contactform-overlay {
position: fixed;
z-index: 100;
height: 100vh;
width: 100%;
padding: 100px;
top: 0px;
left: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.form-control {
background-color: rgba(255,255,255,0.4);
border-radius: 5px;
height: 50px;
}
.form-control::placeholder {
color:#fff;
}
.contactform-close-overlay {
position: relative;
height: 10vh;
}
.contactform-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.contactform-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
.calendly-overlay {
position: absolute;
z-index: 100;
height: 100vh;
width: 100%;
top: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.calendly-close-overlay {
position: relative;
height: 10vh;
}
.calendly-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.calendly-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
#myVideo {
position: fixed;
right: 0;
bottom: 0;
min-width: 100%;
min-height: 100%;
opacity: 0.2;
}
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.navbar-collapse {
height: 100vh;
/*display: flex;
align-items: center;
justify-content: center;*/
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.5em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 4em;
}
.navbar {
background-color: #040206;
color: #fff;
}
.nav-link {
color: #fff !important;
}
.content {
top: 60px;
}
.row {
margin-bottom: 10px;
}
h1 {
color: #64CCC5;
}
p {
color: #EEEEEE;
font-size: 1,1rem;
}
.container-fluid {
margin-bottom:20px;
padding: 20px;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -0,0 +1,536 @@
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/
/*search*/
li {
list-style: none;
}
label {
color: #000;
/* display: none; */
}
.img-fluid {
max-height: 50vh;
width: auto;
}
.pop-img {
border-radius: 10px !important;
border-color: white;
border-width: 1px;
border-style: solid;
}
.card-img-top {
max-height: 50vh;
width: auto;
border-radius: 5px;
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
width: fit-content;
font-weight: bold;
transition: all 0.2s ease;
margin: 15px;
}
.voicebutton {
border-radius: 50% !important;
padding: 10px !important;
width: 40px;
height:40px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 20px;
height: 40px;
transition: all 0.2s ease;
padding: 10px;
margin: 10px;
}
.btn:active {
background: rgba(255, 255, 255, 1);
}
.btn:hover {
background: rgba(255, 255, 255, 0.8);
}
img {
border-radius: 20px !important;
}
input.search_bar{
border: none;
outline: none;
width: 75px;
border-radius: 55px;
margin: 0 auto;
font-size: 1.3em;
color: #0d2840;
padding: 15px 30px 15px 45px;
transition: all .3s cubic-bezier(0,0,.5,1.5);
box-shadow: 0 3px 10px -2px rgba(0,0,0,.1);
background: rgba(255, 255, 255, 0.3) url(https://i.imgur.com/seveWIw.png) no-repeat center center;
}
input.search_bar:focus{
width: 100%;
background-position: calc(100% - 35px) center
}
/*Removes default x in search fields (webkit only i guess)*/
input[type=search]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
/*Changes the color of the placeholder*/
::-webkit-input-placeholder {
color: #0d2840;
opacity: .5;
}
:-moz-placeholder {
color: #0d2840;
opacity: .5;
}
::-moz-placeholder {
color: #0d2840;
opacity: .5;
}
:-ms-input-placeholder {
color: #0d2840;
opacity: .5;
}
/*search*/
/*Search2*/
.searchBox {
width: 60px;
background: rgba(255, 255, 255, 0.3);
height: 60px;
border-radius: 40px;
padding: 10px;
margin: 0 auto;
transition: 0.8s;
}
.searchInput:active > .searchBox{
width:100%
}
.searchInput:focus > .searchBox {
width: 100%
}
.searchInput::placeholder {
color:#fff;
}
.searchBox:hover {
width: 100%;
}
.searchBox:hover > .searchInput {
width: calc(100% - 60px);
padding: 0 6px;
}
.searchBox:hover > .searchButton {
background: white;
color: #2f3640;
}
.searchButton {
color: white;
float: right;
width: 40px;
height: 40px;
border-radius: 50px;
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #0d2840 !important;
float: left;
padding: 0;
color: white;
font-size: 16px;
transition: 0.4s;
line-height: 40px;
width: 0px;
}
/*Search2*/
.event {
border-radius: 20px !important;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
/*card design*/
.card {
border-radius: 20px !important;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
.card:hover {
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1), 0 10px 8px rgba(0, 0, 0, 0.015);
}
.card-body .card-title {
font-family: 'Lato', sans-serif;
font-weight: 700;
letter-spacing: 0.3px;
font-size: 24px;
color: #121212;
}
.card-text {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-size: 15px;
letter-spacing: 0.3px;
color: #4E4E4E;
}
.card .container {
width: 88%;
/*background: #F0EEF8;*/
border-radius: 30px;
/*height: 140px;*/
display: flex;
align-items: center;
justify-content: center;
}
.container:hover > img {
transform: scale(1.2);
}
.container img {
/*padding: 75px;*/
/*margin-top: -40px;
margin-bottom: -40px;*/
transition: 0.4s ease;
cursor: pointer;
}
.btn:hover {
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
}
.btn:focus {
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
}
/*card design*/
/*bg*/
:root {
font-size: 15px;
}
body {
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
/*font-family: 'Quicksand', sans-serif;*/
color: #fff !important;
margin: 0;
min-height: 100vh;
background-color: #000;
/*background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));*/
/*background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;*/
/*background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;*/
background-repeat: no-repeat;
/*animation: 10s movement linear infinite;*/
}
body::after {
content: '';
display: block;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.myspan {
position: relative;
z-index: 10;
display: flex;
min-height: 100vh;
width: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
color: transparent;
text-shadow: 0px 0px 1px rgba(255, 255, 255, .6), 0px 4px 4px rgba(0, 0, 0, .05);
letter-spacing: .2rem;
}
@keyframes movement {
0%, 100% {
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
}
25% {
background-size: 100vmax 100vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 60vmax 60vmax;
background-position: -60vmax -90vmax, 50vmax -40vmax, 0vmax -20vmax, -40vmax -20vmax, 40vmax 60vmax;
}
50% {
background-size: 80vmax 80vmax, 110vmax 110vmax, 80vmax 80vmax, 60vmax 60vmax, 80vmax 80vmax;
background-position: -50vmax -70vmax, 40vmax -30vmax, 10vmax 0vmax, 20vmax 10vmax, 30vmax 70vmax;
}
75% {
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
}
}
/*bg*/
.mytextarea {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(20px);
padding: 10px;
border-radius: 10px;
border-width: 0px;
height: unset !important;
}
.mytextarea:active {
border-width: 0px;
}
.mytextarea:focus-visible {
background-color: rgba(255, 255, 255, 0.5);
border-width: 0px !important;
outline: -webkit-focus-ring-color auto 0px;
outline-color: transparent;
}
.navbar-toggler {
color: #fff;
}
.navbar-brand {
font-size: 1.7rem;
color:#fff;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.form-select > option {
background-color: rgba(255, 255, 255, 0.2)
}
.contactform-overlay {
position: fixed;
z-index: 100;
height: 100vh;
width: 100%;
padding: 100px;
top: 0px;
left: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.form-control {
background-color: rgba(255,255,255,0.4);
border-radius: 5px;
height: 50px;
}
.form-control::placeholder {
color:#fff;
}
.contactform-close-overlay {
position: relative;
height: 10vh;
}
.contactform-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.contactform-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
.calendly-overlay {
position: absolute;
z-index: 100;
height: 100vh;
width: 100%;
top: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.calendly-close-overlay {
position: relative;
height: 10vh;
}
.calendly-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.calendly-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
#myVideo {
position: fixed;
right: 0;
bottom: 0;
min-width: 100%;
min-height: 100%;
opacity: 0.2;
}
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.navbar-collapse {
height: 100vh;
/*display: flex;
align-items: center;
justify-content: center;*/
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.5em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 4em;
}
.navbar {
background-color: #040206;
color: #fff;
}
.nav-link {
color: #fff !important;
}
.content {
top: 60px;
}
.row {
margin-bottom: 10px;
}
h1 {
color: #64CCC5;
}
p {
color: #EEEEEE;
font-size: 1,1rem;
}
.container-fluid {
margin-bottom:20px;
padding: 20px;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,536 @@
/*@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
@import url('https://fonts.googleapis.com/css?family=Comfortaa:400,700,300');*/
/*search*/
li {
list-style: none;
}
label {
color: #000;
/* display: none; */
}
.img-fluid {
max-height: 50vh;
width: auto;
}
.pop-img {
border-radius: 10px !important;
border-color: white;
border-width: 1px;
border-style: solid;
}
.card-img-top {
max-height: 50vh;
width: auto;
border-radius: 5px;
}
.btn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
width: fit-content;
font-weight: bold;
transition: all 0.2s ease;
margin: 15px;
}
.voicebutton {
border-radius: 50% !important;
padding: 10px !important;
width: 40px;
height:40px;
}
.menubtn {
background: rgba(255, 255, 255, 0.5);
border: 0;
color: #000000;
/* width: 98%; */
font-weight: bold;
border-radius: 20px;
height: 40px;
transition: all 0.2s ease;
padding: 10px;
margin: 10px;
}
.btn:active {
background: rgba(255, 255, 255, 1);
}
.btn:hover {
background: rgba(255, 255, 255, 0.8);
}
img {
border-radius: 20px !important;
}
input.search_bar{
border: none;
outline: none;
width: 75px;
border-radius: 55px;
margin: 0 auto;
font-size: 1.3em;
color: #0d2840;
padding: 15px 30px 15px 45px;
transition: all .3s cubic-bezier(0,0,.5,1.5);
box-shadow: 0 3px 10px -2px rgba(0,0,0,.1);
background: rgba(255, 255, 255, 0.3) url(https://i.imgur.com/seveWIw.png) no-repeat center center;
}
input.search_bar:focus{
width: 100%;
background-position: calc(100% - 35px) center
}
/*Removes default x in search fields (webkit only i guess)*/
input[type=search]::-webkit-search-cancel-button {
-webkit-appearance: none;
}
/*Changes the color of the placeholder*/
::-webkit-input-placeholder {
color: #0d2840;
opacity: .5;
}
:-moz-placeholder {
color: #0d2840;
opacity: .5;
}
::-moz-placeholder {
color: #0d2840;
opacity: .5;
}
:-ms-input-placeholder {
color: #0d2840;
opacity: .5;
}
/*search*/
/*Search2*/
.searchBox {
width: 60px;
background: rgba(255, 255, 255, 0.3);
height: 60px;
border-radius: 40px;
padding: 10px;
margin: 0 auto;
transition: 0.8s;
}
.searchInput:active > .searchBox{
width:100%
}
.searchInput:focus > .searchBox {
width: 100%
}
.searchInput::placeholder {
color:#fff;
}
.searchBox:hover {
width: 100%;
}
.searchBox:hover > .searchInput {
width: calc(100% - 60px);
padding: 0 6px;
}
.searchBox:hover > .searchButton {
background: white;
color: #2f3640;
}
.searchButton {
color: white;
float: right;
width: 40px;
height: 40px;
border-radius: 50px;
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
.searchInput {
border: none;
background: none;
outline: none;
font-size: 1.3em !important;
color: #0d2840 !important;
float: left;
padding: 0;
color: white;
font-size: 16px;
transition: 0.4s;
line-height: 40px;
width: 0px;
}
/*Search2*/
.event {
border-radius: 20px !important;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
/*card design*/
.card {
border-radius: 20px !important;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.2) !important;
backdrop-filter: blur(20px);
border: 0;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.07);
transition: all 0.15s ease;
}
.card:hover {
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.1), 0 10px 8px rgba(0, 0, 0, 0.015);
}
.card-body .card-title {
font-family: 'Lato', sans-serif;
font-weight: 700;
letter-spacing: 0.3px;
font-size: 24px;
color: #121212;
}
.card-text {
font-family: 'Lato', sans-serif;
font-weight: 400;
font-size: 15px;
letter-spacing: 0.3px;
color: #4E4E4E;
}
.card .container {
width: 88%;
/*background: #F0EEF8;*/
border-radius: 30px;
/*height: 140px;*/
display: flex;
align-items: center;
justify-content: center;
}
.container:hover > img {
transform: scale(1.2);
}
.container img {
/*padding: 75px;*/
/*margin-top: -40px;
margin-bottom: -40px;*/
transition: 0.4s ease;
cursor: pointer;
}
.btn:hover {
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
}
.btn:focus {
background-color: #e493d0;
background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
background-repeat: no-repeat;
animation: 10s movement linear infinite;
}
/*card design*/
/*bg*/
:root {
font-size: 15px;
}
body {
/*font-family: 'Comfortaa', 'Arial Narrow', Arial, sans-serif;*/
/*font-family: 'Quicksand', sans-serif;*/
color: #fff !important;
margin: 0;
min-height: 100vh;
background-color: #000;
/*background-image: radial-gradient(closest-side, rgba(235, 105, 78, 1), rgba(235, 105, 78, 0)), radial-gradient(closest-side, rgba(243, 11, 164, 1), rgba(243, 11, 164, 0)), radial-gradient(closest-side, rgba(254, 234, 131, 1), rgba(254, 234, 131, 0)), radial-gradient(closest-side, rgba(170, 142, 245, 1), rgba(170, 142, 245, 0)), radial-gradient(closest-side, rgba(248, 192, 147, 1), rgba(248, 192, 147, 0));*/
/*background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;*/
/*background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;*/
background-repeat: no-repeat;
/*animation: 10s movement linear infinite;*/
}
body::after {
content: '';
display: block;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.myspan {
position: relative;
z-index: 10;
display: flex;
min-height: 100vh;
width: 100%;
justify-content: center;
align-items: center;
font-size: 5rem;
color: transparent;
text-shadow: 0px 0px 1px rgba(255, 255, 255, .6), 0px 4px 4px rgba(0, 0, 0, .05);
letter-spacing: .2rem;
}
@keyframes movement {
0%, 100% {
background-size: 130vmax 130vmax, 80vmax 80vmax, 90vmax 90vmax, 110vmax 110vmax, 90vmax 90vmax;
background-position: -80vmax -80vmax, 60vmax -30vmax, 10vmax 10vmax, -30vmax -10vmax, 50vmax 50vmax;
}
25% {
background-size: 100vmax 100vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 60vmax 60vmax;
background-position: -60vmax -90vmax, 50vmax -40vmax, 0vmax -20vmax, -40vmax -20vmax, 40vmax 60vmax;
}
50% {
background-size: 80vmax 80vmax, 110vmax 110vmax, 80vmax 80vmax, 60vmax 60vmax, 80vmax 80vmax;
background-position: -50vmax -70vmax, 40vmax -30vmax, 10vmax 0vmax, 20vmax 10vmax, 30vmax 70vmax;
}
75% {
background-size: 90vmax 90vmax, 90vmax 90vmax, 100vmax 100vmax, 90vmax 90vmax, 70vmax 70vmax;
background-position: -50vmax -40vmax, 50vmax -30vmax, 20vmax 0vmax, -10vmax 10vmax, 40vmax 60vmax;
}
}
/*bg*/
.mytextarea {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(20px);
padding: 10px;
border-radius: 10px;
border-width: 0px;
height: unset !important;
}
.mytextarea:active {
border-width: 0px;
}
.mytextarea:focus-visible {
background-color: rgba(255, 255, 255, 0.5);
border-width: 0px !important;
outline: -webkit-focus-ring-color auto 0px;
outline-color: transparent;
}
.navbar-toggler {
color: #fff;
}
.navbar-brand {
font-size: 1.7rem;
color:#fff;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.form-select > option {
background-color: rgba(255, 255, 255, 0.2)
}
.contactform-overlay {
position: fixed;
z-index: 100;
height: 100vh;
width: 100%;
padding: 100px;
top: 0px;
left: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.form-control {
background-color: rgba(255,255,255,0.4);
border-radius: 5px;
height: 50px;
}
.form-control::placeholder {
color:#fff;
}
.contactform-close-overlay {
position: relative;
height: 10vh;
}
.contactform-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.contactform-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
.calendly-overlay {
position: absolute;
z-index: 100;
height: 100vh;
width: 100%;
top: 0px;
/* padding-top: 10vh; */
backdrop-filter: blur(20px);
/* background-color: rgba(1, 1, 1, .4); */
}
.calendly-close-overlay {
position: relative;
height: 10vh;
}
.calendly-popup-content {
height: 80vh;
margin: 0px;
padding: 0px;
}
.calendly-popup-close {
position: relative;
height: 10vh;
z-index: 80;
}
#myVideo {
position: fixed;
right: 0;
bottom: 0;
min-width: 100%;
min-height: 100%;
opacity: 0.2;
}
.table {
color: #fff !important;
padding-top: 10px;
padding-bottom: 10px;
background-color: transparent;
}
.navbar-collapse {
height: 100vh;
/*display: flex;
align-items: center;
justify-content: center;*/
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.5em;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
border-bottom: 0px solid white;
padding: 0.2em 4em;
}
.navbar {
background-color: #040206;
color: #fff;
}
.nav-link {
color: #fff !important;
}
.content {
top: 60px;
}
.row {
margin-bottom: 10px;
}
h1 {
color: #64CCC5;
}
p {
color: #EEEEEE;
font-size: 1,1rem;
}
.container-fluid {
margin-bottom:20px;
padding: 20px;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -0,0 +1,168 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

View File

@ -0,0 +1,168 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
.card {
background-color: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(2px);
}
.table {
color: #f2d8bb !important;
}
.navbar-collapse {
height: 100vh;
text-align: center !important;
align-content: center;
}
.navbar-collapse .nav-link {
font-size: 1.2rem;
letter-spacing: 2px;
}
.navbar-collapse .nav-item:not(:last-child) {
padding: 0.2em 1em;
}
.navbar {
background-color: #022c28;
color: #d0eae9;
}
.nav-link {
color: #d0eae9 !important;
}
.content {
top: 60px;
}
body {
background-color: #022c28;
background-attachment: fixed;
background-position: center;
background-size: cover;
color: aqua;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 300;
font-style: normal;
font-size: 1rem; /* Base size */
}
h1 {
font-weight: 700;
font-size: 2.5rem;
padding-top: 30px;
padding-bottom: 10px;
}
h2 {
font-weight: 500;
font-size: 2rem;
padding-top: 15px;
padding-bottom: 10px;
}
h3 {
font-weight: 400;
font-size: 1.6rem;
padding-top: 10px;
padding-bottom: 10px;
}
p {
color: #fff;
font-size: 1.1rem;
}
li {
color: #fff;
font-size: 1rem;
text-align: center;
}
.btn-primary {
color: #d0eae9;
background-color: #014d4e;
border: 0px;
margin: 5px;
}
.btn-primary:hover {
color: #fff;
background-color: #086262;
border: 0px;
}
.row {
padding-bottom: 30px;
margin: 0 auto !important;
}
.searchInput::placeholder {
color: #d0eae9;
}
#myVideo {
position: fixed;
right: 0;
top: -100px;
min-width: 100%;
min-height: 100%;
transform: translateX(calc((100% - 100vw) / 2));
}
.img-fluid {
max-height: 50vh;
width: auto;
border-radius: 15px;
}
.sp-img {
box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-webkit-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
-moz-box-shadow: 10px 10px 30px 0px rgba(0,0,0,0.75);
}
.col {
align-items: center;
display: flex;
}
.form-select {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 5px;
display: unset !important;
color: #fff;
}
.list-group-item,
.bg-light {
background-color: rgb(11 24 23 / 76%) !important;
backdrop-filter: blur(8px) !important;
}
.text-primary {
--bs-text-opacity: 1;
color: aqua;
}
/* 🔽 Mobile Responsiveness */
@media (max-width: 768px) {
h1 { font-size: 2rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.4rem; }
p, li { font-size: 1rem; }
.navbar-collapse .nav-link { font-size: 1.1rem; }
p {text-align: justify;}
}
@media (max-width: 480px) {
h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
p, li { font-size: 0.95rem; }
.navbar-collapse .nav-link { font-size: 1rem; }
p {text-align: justify;}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.