diff --git a/st/sei_time/doctype/character/character.js b/st/sei_time/doctype/character/character.js index a06012e..be65eff 100644 --- a/st/sei_time/doctype/character/character.js +++ b/st/sei_time/doctype/character/character.js @@ -1,8 +1,21 @@ // Copyright (c) 2026, Vassili and contributors // For license information, please see license.txt -// frappe.ui.form.on("Character", { -// refresh(frm) { +frappe.ui.form.on("Character", { + // refresh(frm) { -// }, -// }); + // }, + type(frm) { + frm.trigger('last_interaction'); + }, + last_interaction(frm) { + frappe.db.get_value('Character Type', frm.doc.type, 'death_clock') + .then(r => { + if (r && r.message) { + let last_int = moment(frm.doc.last_interaction); + let death = last_int.clone().add(r.message.death_clock, 'days'); + frm.set_value('death', death.format('YYYY-MM-DD HH:mm:ss')); + } + }); + } +}); diff --git a/st/sei_time/doctype/character/character.json b/st/sei_time/doctype/character/character.json index 0acd6e7..43c2c36 100644 --- a/st/sei_time/doctype/character/character.json +++ b/st/sei_time/doctype/character/character.json @@ -8,7 +8,9 @@ "field_order": [ "character_name", "type", - "profile_picture" + "profile_picture", + "last_interaction", + "death" ], "fields": [ { @@ -29,12 +31,24 @@ "fieldname": "profile_picture", "fieldtype": "Attach Image", "label": "Profile Picture" + }, + { + "fieldname": "last_interaction", + "fieldtype": "Datetime", + "label": "Last Interaction", + "permlevel": 2 + }, + { + "fieldname": "death", + "fieldtype": "Datetime", + "label": "Death", + "permlevel": 1 } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2026-03-24 19:37:53.659927", + "modified": "2026-03-31 19:09:46.094796", "modified_by": "Administrator", "module": "Sei Time", "name": "Character", diff --git a/st/sei_time/doctype/character/character.py b/st/sei_time/doctype/character/character.py index b2bd970..82e7f1e 100644 --- a/st/sei_time/doctype/character/character.py +++ b/st/sei_time/doctype/character/character.py @@ -15,6 +15,8 @@ class Character(Document): from frappe.types import DF character_name: DF.Data + death: DF.Datetime | None + last_interaction: DF.Datetime | None profile_picture: DF.AttachImage | None type: DF.Link | None # end: auto-generated types diff --git a/st/sei_time/doctype/character_type/character_type.json b/st/sei_time/doctype/character_type/character_type.json index 13412ce..7f562d9 100644 --- a/st/sei_time/doctype/character_type/character_type.json +++ b/st/sei_time/doctype/character_type/character_type.json @@ -7,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "type", + "death_clock", "frame_image", "background_image" ], @@ -28,12 +29,17 @@ "fieldname": "background_image", "fieldtype": "Attach Image", "label": "Background Image" + }, + { + "fieldname": "death_clock", + "fieldtype": "Int", + "label": "Death Clock (days)" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2026-03-24 18:56:08.479742", + "modified": "2026-03-31 19:03:42.942277", "modified_by": "Administrator", "module": "Sei Time", "name": "Character Type", diff --git a/st/sei_time/doctype/character_type/character_type.py b/st/sei_time/doctype/character_type/character_type.py index 31e3e3c..354e05a 100644 --- a/st/sei_time/doctype/character_type/character_type.py +++ b/st/sei_time/doctype/character_type/character_type.py @@ -15,6 +15,7 @@ class CharacterType(Document): from frappe.types import DF background_image: DF.AttachImage | None + death_clock: DF.Int frame_image: DF.AttachImage | None type: DF.Data # end: auto-generated types diff --git a/st/sei_time/doctype/game/game.js b/st/sei_time/doctype/game/game.js index d87b9f8..0e70dd3 100644 --- a/st/sei_time/doctype/game/game.js +++ b/st/sei_time/doctype/game/game.js @@ -1,8 +1,16 @@ // Copyright (c) 2026, Vassili and contributors // For license information, please see license.txt -// frappe.ui.form.on("Game", { -// refresh(frm) { +frappe.ui.form.on("Game", { + // refresh(frm) { -// }, -// }); + // }, + session_duration(frm) { + frm.trigger('next_session_start'); + }, + next_session_start(frm) { + let start = moment(frm.doc.next_session_start); + let end = start.clone().add(frm.doc.session_duration, 'hours'); + frm.set_value('next_session_end', end.format('YYYY-MM-DD HH:mm:ss')); + } +}); \ No newline at end of file diff --git a/st/sei_time/doctype/game/game.json b/st/sei_time/doctype/game/game.json index 2987d2d..010ddc0 100644 --- a/st/sei_time/doctype/game/game.json +++ b/st/sei_time/doctype/game/game.json @@ -8,11 +8,18 @@ "field_order": [ "title", "description", - "section_break_nsqq", - "next_session", - "max_seats", + "section_break_sial", "type", - "select_vniu" + "max_seats", + "frequency", + "session_duration", + "section_break_next_session", + "next_session_number", + "next_session_start", + "next_session_end", + "schedule_session", + "all_sessions_section", + "all_sessions" ], "fields": [ { @@ -27,15 +34,7 @@ "label": "Description" }, { - "fieldname": "section_break_nsqq", - "fieldtype": "Section Break" - }, - { - "fieldname": "next_session", - "fieldtype": "Datetime", - "label": "Next Session" - }, - { + "default": "6", "fieldname": "max_seats", "fieldtype": "Int", "label": "Max Seats" @@ -47,15 +46,67 @@ "options": "Game Type" }, { - "fieldname": "select_vniu", + "fieldname": "section_break_sial", + "fieldtype": "Section Break" + }, + { + "default": "Weekly", + "fieldname": "frequency", "fieldtype": "Select", - "options": "January\nFebruary\nMarch" + "label": "Frequency", + "options": "Weekly\nBiweekly\nEtc." + }, + { + "fieldname": "section_break_next_session", + "fieldtype": "Section Break", + "label": "Next Session" + }, + { + "default": "0", + "fieldname": "next_session_number", + "fieldtype": "Int", + "label": "Next Session Number" + }, + { + "default": "now", + "fieldname": "next_session_start", + "fieldtype": "Datetime", + "label": "Next Session Start" + }, + { + "default": "now", + "fieldname": "next_session_end", + "fieldtype": "Datetime", + "label": "Next Session End", + "read_only": 1 + }, + { + "fieldname": "session_duration", + "fieldtype": "Float", + "label": "Session Duration (Hours)", + "length": 3, + "non_negative": 1, + "precision": "1" + }, + { + "fieldname": "schedule_session", + "fieldtype": "Button", + "label": "Schedule Session" + }, + { + "fieldname": "all_sessions_section", + "fieldtype": "Section Break", + "label": "All Sessions" + }, + { + "fieldname": "all_sessions", + "fieldtype": "HTML" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2026-03-24 19:02:39.174748", + "modified": "2026-04-28 19:43:24.253848", "modified_by": "Administrator", "module": "Sei Time", "name": "Game", @@ -86,7 +137,7 @@ ], "row_format": "Dynamic", "rows_threshold_for_grid_search": 20, - "sort_field": "next_session", + "sort_field": "next_session_start", "sort_order": "DESC", "states": [] } diff --git a/st/sei_time/doctype/game/game.py b/st/sei_time/doctype/game/game.py index ef303aa..ca6b728 100644 --- a/st/sei_time/doctype/game/game.py +++ b/st/sei_time/doctype/game/game.py @@ -4,7 +4,7 @@ import frappe from frappe.model.document import Document from frappe.utils import get_datetime - +from frappe.types import DF class Game(Document): # begin: auto-generated types @@ -16,9 +16,12 @@ class Game(Document): from frappe.types import DF description: DF.SmallText | None + frequency: DF.Literal["Weekly", "Biweekly", "Etc."] max_seats: DF.Int - next_session: DF.Datetime | None - select_vniu: DF.Literal["January", "February", "March"] + next_session_end: DF.Datetime | None + next_session_number: DF.Int + next_session_start: DF.Datetime | None + session_duration: DF.Float title: DF.Data | None type: DF.Link | None # end: auto-generated types @@ -26,23 +29,22 @@ class Game(Document): pass @frappe.whitelist() -def get_events(start, end, filters=None): +def get_events(start:DF.Datetime, end:DF.Datetime, filters:str=None): event_docs = frappe.get_all( "Game", - fields=["name", "title", "next_session"], + fields=["name", "title", "next_session_start", "next_session_end"], filters=[ - ["next_session", 'between', [start, end]], + ["next_session_start", 'between', [start, end]], ] ) events = [] for doc in event_docs: - next_session = get_datetime(doc.next_session) events.append({ "name": doc.name, "title": doc.title, - "start": next_session, - "end": next_session, + "start": get_datetime(doc.next_session_start), + "end": get_datetime(doc.next_session_end), }) return events \ No newline at end of file diff --git a/st/sei_time/doctype/game/game_calendar.js b/st/sei_time/doctype/game/game_calendar.js index 9974cb4..412b583 100644 --- a/st/sei_time/doctype/game/game_calendar.js +++ b/st/sei_time/doctype/game/game_calendar.js @@ -1,7 +1,7 @@ frappe.views.calendar["Game"] = { field_map: { - "start": "next_session", - "end": "next_session", // Optional + "start": "start", + "end": "end", "id": "name", "title": "title" }, diff --git a/st/sei_time/doctype/game_session/__init__.py b/st/sei_time/doctype/game_session/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/st/sei_time/doctype/game_session/game_session.js b/st/sei_time/doctype/game_session/game_session.js new file mode 100644 index 0000000..d887fb2 --- /dev/null +++ b/st/sei_time/doctype/game_session/game_session.js @@ -0,0 +1,8 @@ +// Copyright (c) 2026, Vassili and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Game Session", { +// refresh(frm) { + +// }, +// }); diff --git a/st/sei_time/doctype/game_session/game_session.json b/st/sei_time/doctype/game_session/game_session.json new file mode 100644 index 0000000..eed09fa --- /dev/null +++ b/st/sei_time/doctype/game_session/game_session.json @@ -0,0 +1,98 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:session_title", + "creation": "2026-04-26 03:29:54.749610", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "game", + "game_title", + "session_title", + "session_number", + "session_start", + "session_end", + "notes_section", + "notes" + ], + "fields": [ + { + "fieldname": "game", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Game", + "options": "Game", + "reqd": 1 + }, + { + "fetch_from": "game.title", + "fieldname": "game_title", + "fieldtype": "Data", + "label": "Game Title", + "read_only": 1 + }, + { + "fieldname": "session_title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Session Title", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "session_number", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Session Number", + "reqd": 1 + }, + { + "fieldname": "session_start", + "fieldtype": "Datetime", + "label": "Session Start" + }, + { + "fieldname": "session_end", + "fieldtype": "Datetime", + "label": "Session End" + }, + { + "collapsible": 1, + "fieldname": "notes_section", + "fieldtype": "Section Break", + "label": "Notes" + }, + { + "fieldname": "notes", + "fieldtype": "Long Text" + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2026-04-28 19:53:33.962004", + "modified_by": "Administrator", + "module": "Sei Time", + "name": "Game Session", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "row_format": "Dynamic", + "rows_threshold_for_grid_search": 20, + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/st/sei_time/doctype/game_session/game_session.py b/st/sei_time/doctype/game_session/game_session.py new file mode 100644 index 0000000..d274b6b --- /dev/null +++ b/st/sei_time/doctype/game_session/game_session.py @@ -0,0 +1,26 @@ +# Copyright (c) 2026, Vassili and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class GameSession(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + game: DF.Link + game_title: DF.Data | None + notes: DF.LongText | None + session_end: DF.Datetime | None + session_number: DF.Int + session_start: DF.Datetime | None + session_title: DF.Data + # end: auto-generated types + + pass diff --git a/st/sei_time/doctype/game_session/test_game_session.py b/st/sei_time/doctype/game_session/test_game_session.py new file mode 100644 index 0000000..47ae88d --- /dev/null +++ b/st/sei_time/doctype/game_session/test_game_session.py @@ -0,0 +1,22 @@ +# Copyright (c) 2026, Vassili and Contributors +# See license.txt + +# import frappe +from frappe.tests import IntegrationTestCase + + +# On IntegrationTestCase, the doctype test records and all +# link-field test record dependencies are recursively loaded +# Use these module variables to add/remove to/from that list +EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] +IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] + + + +class IntegrationTestGameSession(IntegrationTestCase): + """ + Integration tests for GameSession. + Use this class for testing interactions between multiple components. + """ + + pass diff --git a/st/sei_time/page/characters/characters.css b/st/sei_time/page/characters/characters.css index 2aef4e3..9ade006 100644 --- a/st/sei_time/page/characters/characters.css +++ b/st/sei_time/page/characters/characters.css @@ -18,10 +18,131 @@ } .image-stack .fg { - padding: 35px; + padding: 50px; z-index: 2; } .image-stack .frame { z-index: 3; +} + +.deathclock-wrapper { + min-height: 150px; + margin: 0; + display: grid; + place-items: center; + font-family: "Courier New", monospace; + overflow: hidden; +} + +.deathclock { + display: flex; + gap: 5px; + padding: 5px; +} + +.digit { + position: relative; + width: 50px; + height: 90px; + perspective: 700px; +} + +.flipcard { + position: absolute; + inset: 0; + border-radius: 10px; + background: linear-gradient(#1d1d1d, #050505); + color: #f2e7c9; + font-size: 56px; + font-weight: bold; + line-height: 95px; + text-align: center; + overflow: hidden; +} + +.flipcard::before { + content: ""; + position: absolute; + left: 0; + right: 0; + top: 50%; + height: 2px; + background: #000; + box-shadow: + 0 -1px rgba(255,255,255,0.08), + 0 1px rgba(255,255,255,0.05); + z-index: 3; +} + +.top, .bottom { + position: absolute; + left: 0; + width: 100%; + height: 50%; + overflow: hidden; + background: linear-gradient(#202020, #080808); + color: #f2e7c9; + font-size: 56px; + font-weight: bold; + text-align: center; + backface-visibility: hidden; +} + +.top { + top: 0; + line-height: 90px; + border-radius: 10px 10px 0 0; + transform-origin: bottom; +} + +.bottom { + bottom: 0; + line-height: 0; + border-radius: 0 0 10px 10px; + transform-origin: top; +} + +.flip-top { + animation: flipTop 0.28s ease-in forwards; + z-index: 5; +} + +.flip-bottom { + animation: flipBottom 0.28s ease-out forwards; + z-index: 5; +} + +@keyframes flipTop { + from { + transform: rotateX(0deg); + } + to { + transform: rotateX(-90deg); + } +} + +@keyframes flipBottom { + from { + transform: rotateX(90deg); + } + to { + transform: rotateX(0deg); + } +} + +.shine { + position: absolute; + inset: 0; + border-radius: 10px; + pointer-events: none; + background: + linear-gradient( + 115deg, + rgba(255,255,255,0.12), + transparent 35%, + transparent 70%, + rgba(255,255,255,0.05) + ); + z-index: 10; } \ No newline at end of file diff --git a/st/sei_time/page/characters/characters.js b/st/sei_time/page/characters/characters.js index 8d19723..d36078a 100644 --- a/st/sei_time/page/characters/characters.js +++ b/st/sei_time/page/characters/characters.js @@ -6,24 +6,117 @@ frappe.pages['characters'].on_page_load = function(wrapper) { }); frappe.db.get_list('Character', { - fields: ['name', 'type', 'profile_picture', 'type.frame_image', 'type.background_image'], + fields: ['name', 'type', 'profile_picture', 'type.frame_image', 'type.background_image', 'death'], filters: { - owner: frappe.session.user + //owner: frappe.session.user } }).then(docs => { - console.log(docs); render_cards(docs, wrapper); + + const deathclocks = document.getElementsByClassName("deathclock"); + + function createDigit() { + const digit = document.createElement("div"); + digit.className = "digit"; + digit.dataset.value = "0"; + + digit.innerHTML = ` +
0
+
+ `; + + return digit; + } + + function flipDigit(digit, nextValue) { + const currentValue = digit.dataset.value; + if (currentValue === String(nextValue)) return; + + const flipcard = digit.querySelector(".flipcard"); + + const topFlip = document.createElement("div"); + topFlip.className = "top flip-top"; + topFlip.textContent = currentValue; + + const bottomFlip = document.createElement("div"); + bottomFlip.className = "bottom flip-bottom"; + bottomFlip.textContent = nextValue; + + digit.appendChild(topFlip); + + setTimeout(() => { + flipcard.textContent = nextValue; + digit.appendChild(bottomFlip); + }, 140); + + setTimeout(() => { + topFlip.remove(); + bottomFlip.remove(); + digit.dataset.value = String(nextValue); + }, 320); + } + + function countUpDigit(digit, target, delay = 0) { + let value = 0; + + setTimeout(() => { + const timer = setInterval(() => { + if (value >= target) { + clearInterval(timer); + return; + } + + value++; + flipDigit(digit, value % 10); + }, 360); + }, delay); + } + + function initDisplay(clock) { + const value = clock.dataset.value || "0000"; + clock.innerHTML = ""; + + const digits = value.padStart(4, "0").slice(-4).split(""); + + digits.forEach((targetDigit, index) => { + const digit = createDigit(); + clock.appendChild(digit); + + countUpDigit( + digit, + Number(targetDigit), + index * 160 + ); + }); + } + for (let i = 0; i < deathclocks.length; i++) { + initDisplay(deathclocks.item(i)); + } }); } function render_cards(docs, wrapper) { const container = $('
'); + function days_until(deathdate) { + const targetDate = new Date(deathdate.replace(' ', 'T')); + const now = new Date(); + + const diffMs = targetDate - now; + + const diffDays = diffMs / (1000 * 60 * 60 * 24); + + return Math.round(diffDays); + } + docs.forEach(doc => { const card = $(`
-
${doc.name}
+
+
+
+
${doc.name}

${doc.type}

diff --git a/st/sei_time/web_template/__init__.py b/st/sei_time/web_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/st/sei_time/web_template/section_with_bullets/__init__.py b/st/sei_time/web_template/section_with_bullets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/st/sei_time/web_template/section_with_bullets/section_with_bullets.html b/st/sei_time/web_template/section_with_bullets/section_with_bullets.html new file mode 100644 index 0000000..e69de29 diff --git a/st/sei_time/web_template/section_with_bullets/section_with_bullets.json b/st/sei_time/web_template/section_with_bullets/section_with_bullets.json new file mode 100644 index 0000000..82a9a1e --- /dev/null +++ b/st/sei_time/web_template/section_with_bullets/section_with_bullets.json @@ -0,0 +1,36 @@ +{ + "__unsaved": 1, + "creation": "2026-03-31 21:28:46.529250", + "docstatus": 0, + "doctype": "Web Template", + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 0 + }, + { + "fieldname": "subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 0 + }, + { + "fieldname": "bullets", + "fieldtype": "Table Break", + "label": "Bullets", + "options": "", + "reqd": 0 + } + ], + "idx": 0, + "modified": "2026-03-31 21:34:31.754127", + "modified_by": "Administrator", + "module": "Sei Time", + "name": "Section with Bullets", + "owner": "Administrator", + "standard": 1, + "template": "", + "type": "Section" +} diff --git a/st/sei_time/website_theme/custom/custom.json b/st/sei_time/website_theme/custom/custom.json index 039d738..62eaf71 100644 --- a/st/sei_time/website_theme/custom/custom.json +++ b/st/sei_time/website_theme/custom/custom.json @@ -6,16 +6,16 @@ "creation": "2026-03-17 20:43:59.339705", "custom": 0, "custom_overrides": "", - "custom_scss": ".web-footer {\r\n background-color: #2c003e;\r\n color: gold;\r\n padding: 1rem;\r\n text-align: center;\r\n margin-top: auto;\r\n}\r\n\r\n.footer-link {\r\n margin: 0 0.5rem;\r\n}\r\n\r\n.footer-separator {\r\n margin: 0 0.5rem;\r\n}\r\n\r\n.footer-info {\r\n font-size: 0.9rem;\r\n}", - "dark_color": "Primary", + "custom_scss": "body {\n background-color: $body-bg !important;\n}\n\n.navbar,\n.navbar-main,\n.web-footer,\n.navbar .container,\n.web-footer .container {\n background-color: $dark !important;\n}\n\n.navbar, .web-footer {\n color: $body-text-color;\n text-align: center;\n}\n.web-footer {\n margin-top: auto;\n padding: 1rem;\n}\n.footer-link {\n margin: 0 0.5rem;\n}\n.footer-separator {\n margin: 0 0.5rem;\n}\n.footer-info {\n font-size: 0.9rem;\n}\n\n.navbar-nav .nav-link.active,\n.navbar .nav-item.active > a,\n.nav-item a.current,\n.navbar-nav .nav-link[aria-current=\"page\"],\na.nav-link.active {\n color: #fefce8 !important;\n text-shadow: \n 0 0 8px $light,\n 0 0 16px $light,\n 0 0 24px $light !important;\n transition: all 0.4s ease;\n position: relative;\n font-weight: 600;\n}\n\n.navbar-nav .nav-link.active::after {\n content: '';\n position: absolute;\n bottom: -2px;\n left: 50%;\n transform: translateX(-50%);\n width: 60%;\n height: 2px;\n background: linear-gradient(to right, transparent, $light, transparent);\n box-shadow: 0 0 12px $light;\n opacity: 0.9;\n}\n\n.navbar-nav .nav-link.active:hover,\n.navbar-nav .nav-link[aria-current=\"page\"]:hover {\n animation: gold-shimmer 2.2s infinite linear;\n text-shadow: \n 0 0 10px $light,\n 0 0 20px $light,\n 0 0 30px $light;\n}\n\n@keyframes gold-shimmer {\n 0% { text-shadow: 0 0 8px $light, 0 0 16px $light, 0 0 24px $light; }\n 50% { text-shadow: 0 0 12px $light, 0 0 25px $light, 0 0 40px $light; }\n 100% { text-shadow: 0 0 8px $light, 0 0 16px $light, 0 0 24px $light; }\n}\n\n.navbar-nav .nav-link:hover:not(.active) {\n color: $light !important;\n text-shadow: 0 0 8px $light;\n}\n\nh1 {\n color: #e0b030 !important;\n}\n\nh2, h3, h4, h5, h6,\n.lead, .subtitle, .welcome-text,\nstrong, b {\n color: #c0262e !important;\n}\n\np, span, div, td, th,\n.text-muted, .text-secondary, .text-light,\n.small, small, .muted {\n color: $light !important;\n}\n\n.user-dropdown,\n.dropdown-menu,\n.user-dropdown-menu,\n.nav-item .dropdown-menu {\n background-color: $dark !important;\n border: 2px solid $light !important;\n border-radius: 8px !important;\n box-shadow: 0 4px 15px rgba(252, 211, 77, 0.3) !important;\n padding: 0.5rem 0 !important;\n}\n\n.user-dropdown a,\n.user-dropdown button,\n.dropdown-menu a,\n.dropdown-menu button,\n.user-dropdown-menu a {\n color: $light !important;\n background-color: transparent !important;\n padding: 0.65rem 1.25rem !important;\n font-weight: 500;\n}\n\n.user-dropdown a:hover,\n.user-dropdown button:hover,\n.dropdown-menu a:hover,\n.dropdown-menu button:hover {\n background-color: $primary !important;\n color: #fefce8 !important;\n border-radius: 4px;\n}\n\nli,\nul li,\nol li {\n color: #d1d5db !important;\n}\n\n.navbar-nav .nav-link,\n.navbar .nav-link,\n.navbar a {\n color: $body-text-color !important;\n}\n\na:not(.nav-link, .footer-link) {\n color: lighten($body-text-color, 30%) !important;\n font-weight: bolder;\n font-variant: small-caps;\n}\n\na:not(.nav-link, .footer-link):hover {\n color: #fefce8 !important;\n text-shadow: 0 0 8px $light;\n}", + "dark_color": "Dark Purple", "docstatus": 0, "doctype": "Website Theme", "font_properties": "wght@400..900", "google_font": "Orbitron", "idx": 0, "ignored_apps": [], - "light_color": "Gold Text", - "modified": "2026-03-24 19:13:02.880572", + "light_color": "Light Gold", + "modified": "2026-04-14 21:27:58.487894", "modified_by": "Administrator", "module": "Sei Time", "name": "Custom", @@ -23,6 +23,6 @@ "primary_color": "Primary", "text_color": "Gold Text", "theme": "Custom", - "theme_scss": "\n@import url(\"https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap\");\n// backward compatibility. deprecated in v15\n$font-family-sans-serif: \"Orbitron\", \"InterVariable\", \"Inter\", -apple-system, BlinkMacSystemFont,\n\t\"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\",\n\t\"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\n// override font stack if custom font is set in website theme\n:root {\n\t--font-stack: \"Orbitron\", \"InterVariable\", \"Inter\", -apple-system, BlinkMacSystemFont,\n\t\"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\",\n\t\"Droid Sans\", \"Helvetica Neue\", sans-serif !important;\n}\n$primary: #8139c4;$dark: #8139c4;$body-text-color: #FFD700;$body-bg: #4e3269;$enable-shadows: false;\n$enable-gradients: false;\n$enable-rounded: true;\n\n// Bootstrap Variable Overrides\n\n\n// Import themes from installed apps\n@import \"frappe/public/scss/website.bundle\";\n\n\n\n// Custom Theme\n.web-footer {\r\n background-color: #2c003e;\r\n color: gold;\r\n padding: 1rem;\r\n text-align: center;\r\n margin-top: auto;\r\n}\r\n\r\n.footer-link {\r\n margin: 0 0.5rem;\r\n}\r\n\r\n.footer-separator {\r\n margin: 0 0.5rem;\r\n}\r\n\r\n.footer-info {\r\n font-size: 0.9rem;\r\n}\n\n:root {\n\t\n\t--primary: #{$primary};\n\t--primary-color: #{$primary};\n\t\n\t--bg-color: #{$body-bg};\n\t\n\t--text-color: #{$body-text-color};\n\t--text-light: #{$body-text-color};\n\t}\n", + "theme_scss": "\n@import url(\"https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap\");\n// backward compatibility. deprecated in v15\n$font-family-sans-serif: \"Orbitron\", \"InterVariable\", \"Inter\", -apple-system, BlinkMacSystemFont,\n\t\"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\",\n\t\"Droid Sans\", \"Helvetica Neue\", sans-serif;\n\n// override font stack if custom font is set in website theme\n:root {\n\t--font-stack: \"Orbitron\", \"InterVariable\", \"Inter\", -apple-system, BlinkMacSystemFont,\n\t\"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\",\n\t\"Droid Sans\", \"Helvetica Neue\", sans-serif !important;\n}\n$primary: #4c1d95;$body-text-color: #facc15;$light: #fcd34d;$dark: #2c003e;$body-bg: #1f002b;$enable-shadows: false;\n$enable-gradients: false;\n$enable-rounded: true;\n\n// Bootstrap Variable Overrides\n\n\n// Import themes from installed apps\n@import \"frappe/public/scss/website.bundle\";\n\n\n\n// Custom Theme\nbody {\n background-color: $body-bg !important;\n}\n\n.navbar,\n.navbar-main,\n.web-footer,\n.navbar .container,\n.web-footer .container {\n background-color: $dark !important;\n}\n\n.navbar, .web-footer {\n color: $body-text-color;\n text-align: center;\n}\n.web-footer {\n margin-top: auto;\n padding: 1rem;\n}\n.footer-link {\n margin: 0 0.5rem;\n}\n.footer-separator {\n margin: 0 0.5rem;\n}\n.footer-info {\n font-size: 0.9rem;\n}\n\n.navbar-nav .nav-link.active,\n.navbar .nav-item.active > a,\n.nav-item a.current,\n.navbar-nav .nav-link[aria-current=\"page\"],\na.nav-link.active {\n color: #fefce8 !important;\n text-shadow: \n 0 0 8px $light,\n 0 0 16px $light,\n 0 0 24px $light !important;\n transition: all 0.4s ease;\n position: relative;\n font-weight: 600;\n}\n\n.navbar-nav .nav-link.active::after {\n content: '';\n position: absolute;\n bottom: -2px;\n left: 50%;\n transform: translateX(-50%);\n width: 60%;\n height: 2px;\n background: linear-gradient(to right, transparent, $light, transparent);\n box-shadow: 0 0 12px $light;\n opacity: 0.9;\n}\n\n.navbar-nav .nav-link.active:hover,\n.navbar-nav .nav-link[aria-current=\"page\"]:hover {\n animation: gold-shimmer 2.2s infinite linear;\n text-shadow: \n 0 0 10px $light,\n 0 0 20px $light,\n 0 0 30px $light;\n}\n\n@keyframes gold-shimmer {\n 0% { text-shadow: 0 0 8px $light, 0 0 16px $light, 0 0 24px $light; }\n 50% { text-shadow: 0 0 12px $light, 0 0 25px $light, 0 0 40px $light; }\n 100% { text-shadow: 0 0 8px $light, 0 0 16px $light, 0 0 24px $light; }\n}\n\n.navbar-nav .nav-link:hover:not(.active) {\n color: $light !important;\n text-shadow: 0 0 8px $light;\n}\n\nh1 {\n color: #e0b030 !important;\n}\n\nh2, h3, h4, h5, h6,\n.lead, .subtitle, .welcome-text,\nstrong, b {\n color: #c0262e !important;\n}\n\np, span, div, td, th,\n.text-muted, .text-secondary, .text-light,\n.small, small, .muted {\n color: $light !important;\n}\n\n.user-dropdown,\n.dropdown-menu,\n.user-dropdown-menu,\n.nav-item .dropdown-menu {\n background-color: $dark !important;\n border: 2px solid $light !important;\n border-radius: 8px !important;\n box-shadow: 0 4px 15px rgba(252, 211, 77, 0.3) !important;\n padding: 0.5rem 0 !important;\n}\n\n.user-dropdown a,\n.user-dropdown button,\n.dropdown-menu a,\n.dropdown-menu button,\n.user-dropdown-menu a {\n color: $light !important;\n background-color: transparent !important;\n padding: 0.65rem 1.25rem !important;\n font-weight: 500;\n}\n\n.user-dropdown a:hover,\n.user-dropdown button:hover,\n.dropdown-menu a:hover,\n.dropdown-menu button:hover {\n background-color: $primary !important;\n color: #fefce8 !important;\n border-radius: 4px;\n}\n\nli,\nul li,\nol li {\n color: #d1d5db !important;\n}\n\n.navbar-nav .nav-link,\n.navbar .nav-link,\n.navbar a {\n color: $body-text-color !important;\n}\n\na:not(.nav-link, .footer-link) {\n color: lighten($body-text-color, 30%) !important;\n font-weight: bolder;\n font-variant: small-caps;\n}\n\na:not(.nav-link, .footer-link):hover {\n color: #fefce8 !important;\n text-shadow: 0 0 8px $light;\n}\n\n:root {\n\t\n\t--primary: #{$primary};\n\t--primary-color: #{$primary};\n\t\n\t--bg-color: #{$body-bg};\n\t\n\t--text-color: #{$body-text-color};\n\t--text-light: #{$body-text-color};\n\t}\n", "theme_url": "/files/website_theme/custom_style.css" }