From 8c6abeb8a7f364c48f55885de7160b4aaa48152c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 9 Apr 2026 18:45:07 +0000 Subject: [PATCH] Autosave: 20260409-184507 --- admin_library_profile.php | 236 ++++ assets/css/custom.css | 1087 +++++++++++++---- db/migrations/007_create_reader_activity.sql | 47 + db/migrations/008_create_library_settings.sql | 29 + document.php | 3 + includes/admin_layout.php | 68 +- includes/layout.php | 162 ++- includes/library.php | 646 +++++++++- index.php | 287 +++-- user.php | 302 +++++ viewer.php | 6 + 11 files changed, 2466 insertions(+), 407 deletions(-) create mode 100644 admin_library_profile.php create mode 100644 db/migrations/007_create_reader_activity.sql create mode 100644 db/migrations/008_create_library_settings.sql create mode 100644 user.php diff --git a/admin_library_profile.php b/admin_library_profile.php new file mode 100644 index 0000000..a01bf19 --- /dev/null +++ b/admin_library_profile.php @@ -0,0 +1,236 @@ +getMessage(); + } +} + +$profile = library_get_profile(); +$fields = [ + 'library_name_en', 'library_name_ar', 'short_name', 'tagline_en', 'tagline_ar', + 'description_en', 'description_ar', 'contact_email', 'contact_phone', 'whatsapp_number', + 'website_url', 'address_en', 'address_ar', 'opening_hours_en', 'opening_hours_ar', + 'facebook_url', 'instagram_url', 'x_url', 'youtube_url', 'copyright_text_en', 'copyright_text_ar' +]; +foreach ($fields as $field) { + if (isset($_POST[$field])) { + $profile[$field] = trim((string) $_POST[$field]); + } +} + +admin_render_header($pageTitle, 'library_profile'); +?> + +
+
+ +
+ + +
+
+

+
+
+ + + +
+ +
+
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+ + Library logo + +
+ +
+
+ +
+ + +
+
+ + Favicon + +
+ +
+
+
+
+ +
+
+
+
    +
  • +
  • +
  • +
  • +
+
+
+ +
+ +
+
+
+ + diff --git a/assets/css/custom.css b/assets/css/custom.css index f97c67a..7451602 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,20 +1,28 @@ :root { - --bg: #f5f6f7; - --surface: #ffffff; - --surface-muted: #f0f2f4; - --border: #d8dde3; - --border-strong: #bcc5cf; - --text: #111827; - --text-secondary: #5b6573; - --accent: #1f2937; - --accent-soft: #eef1f4; + --bg: #10151f; + --bg-elevated: #161d29; + --surface: rgba(248, 242, 232, 0.95); + --surface-strong: #f7efe3; + --surface-muted: rgba(255, 248, 240, 0.74); + --surface-deep: rgba(20, 27, 39, 0.84); + --border: rgba(98, 116, 146, 0.22); + --border-strong: rgba(52, 68, 96, 0.28); + --text: #19202b; + --text-secondary: #5b6473; + --text-on-dark: #eef3fb; + --accent: #51d0ff; + --accent-strong: #1097c8; + --accent-warm: #ff9c52; + --accent-soft: rgba(81, 208, 255, 0.12); --success: #0f766e; --warning: #92400e; - --radius-sm: 0.5rem; - --radius-md: 0.75rem; - --radius-lg: 1rem; - --shadow-sm: 0 8px 24px rgba(15, 23, 42, 0.04); - --shadow-md: 0 18px 40px rgba(15, 23, 42, 0.06); + --radius-sm: 0.7rem; + --radius-md: 1.1rem; + --radius-lg: 1.6rem; + --radius-xl: 2rem; + --shadow-sm: 0 18px 40px rgba(5, 10, 20, 0.08); + --shadow-md: 0 30px 80px rgba(5, 10, 20, 0.18); + --shadow-glow: 0 0 0 1px rgba(255,255,255,0.04), 0 24px 80px rgba(10, 20, 40, 0.45); } html { @@ -22,87 +30,302 @@ html { } body { - background: var(--bg); + background: + radial-gradient(circle at top left, rgba(81, 208, 255, 0.18), transparent 24%), + radial-gradient(circle at 85% 12%, rgba(255, 156, 82, 0.16), transparent 20%), + linear-gradient(180deg, #0e141d 0%, #151d29 48%, #0f141d 100%); color: var(--text); - font-family: Inter, "Noto Sans Arabic", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Tahoma, sans-serif; + font-family: Inter, "Cairo", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Tahoma, sans-serif; min-height: 100vh; } [lang="ar"], [dir="rtl"] { - font-family: "Noto Sans Arabic", "Segoe UI", Tahoma, Arial, sans-serif; - line-height: 1.75; + font-family: "Cairo", "Segoe UI", Tahoma, Arial, sans-serif; + line-height: 1.85; text-align: start; unicode-bidi: plaintext; } [lang="ar"] .display-6, +[lang="ar"] .display-5, [lang="ar"] .h3, [lang="ar"] .h4, [lang="ar"] .h5, [dir="rtl"] .display-6, +[dir="rtl"] .display-5, [dir="rtl"] .h3, [dir="rtl"] .h4, [dir="rtl"] .h5 { letter-spacing: 0; } +[lang="ar"] h1, +[lang="ar"] h2, +[lang="ar"] h3, +[lang="ar"] h4, +[lang="ar"] h5, +[lang="ar"] h6, +[dir="rtl"] h1, +[dir="rtl"] h2, +[dir="rtl"] h3, +[dir="rtl"] h4, +[dir="rtl"] h5, +[dir="rtl"] h6 { + font-family: "Cairo", "Segoe UI", Tahoma, Arial, sans-serif; + font-weight: 700; + line-height: 1.45; +} + ::selection { - background: #dbe2ea; + background: rgba(81, 208, 255, 0.28); } .app-shell { min-height: 100vh; + position: relative; + overflow: clip; } -.navbar { - backdrop-filter: blur(8px); +.library-backdrop { + position: fixed; + inset: 0; + pointer-events: none; + z-index: 0; +} + +.backdrop-orb, +.backdrop-column, +.backdrop-grid, +.backdrop-books, +.backdrop-scripture { + position: absolute; +} + +.backdrop-orb { + border-radius: 999px; + filter: blur(8px); + opacity: 0.9; +} + +.orb-cyan { + width: 28rem; + height: 28rem; + top: -8rem; + left: -6rem; + background: radial-gradient(circle, rgba(81, 208, 255, 0.30) 0%, rgba(81, 208, 255, 0.05) 58%, transparent 74%); +} + +.orb-amber { + width: 22rem; + height: 22rem; + right: -4rem; + top: 18rem; + background: radial-gradient(circle, rgba(255, 156, 82, 0.22) 0%, rgba(255, 156, 82, 0.03) 62%, transparent 78%); +} + +.backdrop-grid { + inset: 0; + background-image: + linear-gradient(rgba(255,255,255,0.04) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,0.04) 1px, transparent 1px); + background-size: 72px 72px; + mask-image: linear-gradient(180deg, rgba(0,0,0,0.35), transparent 70%); + opacity: 0.35; +} + +.backdrop-books { + inset-inline: clamp(1rem, 3vw, 3rem); + bottom: 1.5rem; + height: min(46vw, 27rem); + opacity: 0.24; + background-repeat: no-repeat; + background-position: left bottom, right 5% bottom 1rem; + background-size: min(40rem, 52vw) auto, min(32rem, 42vw) auto; + background-image: + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 900 520'%3E%3Cdefs%3E%3ClinearGradient id='page' x1='0' x2='1' y1='0' y2='1'%3E%3Cstop stop-color='%23fff7ea'/%3E%3Cstop offset='1' stop-color='%23d8c0a2'/%3E%3C/linearGradient%3E%3ClinearGradient id='cover' x1='0' x2='1' y1='0' y2='1'%3E%3Cstop stop-color='%236a4127'/%3E%3Cstop offset='1' stop-color='%231e1712'/%3E%3C/linearGradient%3E%3ClinearGradient id='shadow' x1='0' x2='0' y1='0' y2='1'%3E%3Cstop stop-color='%23000000' stop-opacity='.34'/%3E%3Cstop offset='1' stop-color='%23000000' stop-opacity='0'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cellipse cx='440' cy='438' rx='300' ry='44' fill='%23080b11' fill-opacity='.4'/%3E%3Cpath d='M122 357c49-109 125-160 236-180 38-7 86 6 121 28 32 20 56 52 76 104-110-29-219-24-336 12-31 10-61 22-97 36z' fill='url(%23cover)'/%3E%3Cpath d='M568 312c41-48 78-77 118-88 42-12 77-6 109 19 21 16 38 42 51 78-57-11-116-12-178-4-49 7-84 4-100-5z' fill='url(%23cover)' opacity='.92'/%3E%3Cpath d='M160 344c87-92 167-130 268-138 59-4 110 17 152 65-61 5-126 14-193 31-73 18-145 38-227 42z' fill='url(%23page)'/%3E%3Cpath d='M573 305c65-52 124-71 180-58 35 8 64 29 87 66-35-2-71-1-107 2-56 6-109 16-160 31z' fill='url(%23page)'/%3E%3Cpath d='M426 215c5 51 10 97 18 137' stroke='%23825f43' stroke-width='10' stroke-linecap='round' opacity='.55'/%3E%3Cg stroke='%239d7753' stroke-width='5' stroke-linecap='round' opacity='.45'%3E%3Cpath d='M240 298c49-24 95-35 139-34'/%3E%3Cpath d='M223 324c63-27 124-39 183-38'/%3E%3Cpath d='M207 351c77-28 152-39 225-36'/%3E%3Cpath d='M631 287c39-13 77-18 114-13'/%3E%3Cpath d='M616 311c54-16 107-22 157-18'/%3E%3Cpath d='M600 337c66-16 127-19 183-12'/%3E%3C/g%3E%3Cg fill='none' stroke='%23623f2a' stroke-width='4' opacity='.55'%3E%3Cpath d='M275 287c26-15 55-22 88-18'/%3E%3Cpath d='M295 313c34-14 67-16 100-8'/%3E%3Cpath d='M312 338c37-13 72-13 106-3'/%3E%3Cpath d='M655 286c23-7 46-6 68 2'/%3E%3Cpath d='M669 311c30-9 59-8 87 4'/%3E%3Cpath d='M684 336c30-7 58-4 84 9'/%3E%3C/g%3E%3Cpath d='M146 356c103 8 200-14 292-41 63-19 120-30 172-34' stroke='url(%23shadow)' stroke-width='24' stroke-linecap='round' opacity='.35'/%3E%3C/svg%3E"), + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 760 460'%3E%3Cdefs%3E%3ClinearGradient id='page2' x1='0' x2='1' y1='0' y2='1'%3E%3Cstop stop-color='%23fbf1df'/%3E%3Cstop offset='1' stop-color='%23cfb18b'/%3E%3C/linearGradient%3E%3ClinearGradient id='cover2' x1='0' x2='1' y1='0' y2='1'%3E%3Cstop stop-color='%23502d1c'/%3E%3Cstop offset='1' stop-color='%23130f0c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cellipse cx='384' cy='404' rx='214' ry='32' fill='%23080b11' fill-opacity='.32'/%3E%3Cpath d='M168 303c76-65 140-95 212-101 49-4 93 12 131 49-49 5-102 15-158 31-58 17-120 27-185 21z' fill='url(%23page2)'/%3E%3Cpath d='M504 283c39-39 80-60 124-65 38-5 72 11 102 48-28 0-61 5-100 14-39 10-81 18-126 25z' fill='url(%23page2)'/%3E%3Cpath d='M156 316c40-53 85-85 133-96 52-13 106-5 163 25-71 3-139 16-204 39-25 9-56 20-92 32z' fill='url(%23cover2)' opacity='.95'/%3E%3Cpath d='M502 289c44-31 86-46 126-44 31 1 58 17 82 47-32 1-67 6-103 16-34 10-69 17-105 22z' fill='url(%23cover2)' opacity='.95'/%3E%3Cg fill='none' stroke='%23765437' stroke-width='4' stroke-linecap='round' opacity='.45'%3E%3Cpath d='M244 274c38-18 76-25 116-19'/%3E%3Cpath d='M224 298c56-18 110-24 163-18'/%3E%3Cpath d='M208 325c66-18 128-23 188-14'/%3E%3Cpath d='M550 270c27-7 54-8 81-2'/%3E%3Cpath d='M539 296c38-8 74-7 108 3'/%3E%3Cpath d='M529 321c46-8 89-4 129 10'/%3E%3C/g%3E%3Cg fill='none' stroke='%23573a2a' stroke-width='3.5' opacity='.52'%3E%3Cpath d='M270 262c20-13 42-17 66-13'/%3E%3Cpath d='M282 287c28-11 54-12 81-5'/%3E%3Cpath d='M294 311c29-10 56-9 83-1'/%3E%3Cpath d='M576 267c17-5 34-4 50 2'/%3E%3Cpath d='M584 291c22-6 43-4 64 4'/%3E%3Cpath d='M592 315c23-5 45-2 66 7'/%3E%3C/svg%3E"); + filter: saturate(0.9) blur(0.2px); + mix-blend-mode: screen; +} + +.backdrop-scripture { + inset-inline: 5%; + bottom: 0; + height: min(38vw, 20rem); + opacity: 0.14; + background-repeat: repeat-x; + background-position: center bottom; + background-size: min(36rem, 46vw) auto; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 900 160'%3E%3Cg fill='none' stroke='%23f8d7b2' stroke-linecap='round' stroke-width='5' opacity='.72'%3E%3Cpath d='M40 96c19-18 34-27 49-29 14-2 30 3 47 16 18 13 34 15 48 4 12-9 18-17 27-38 5 28 18 45 38 50 20 5 41-3 62-26 14-15 29-22 46-21 16 1 29 9 39 24 13 20 29 30 48 29 23-2 44-17 65-47 10 17 21 29 33 35 23 11 48 4 75-21 21-19 41-28 61-27 19 2 35 15 50 41 13 23 31 34 54 35 22 1 43-12 64-38 16-21 35-30 56-28 18 2 35 14 49 36'/%3E%3Cpath d='M24 122c24-15 42-21 56-17 16 4 30 16 42 35 12-22 28-32 47-30 18 3 33 15 46 37 10-19 23-28 39-28 15 0 28 9 40 26 11-18 25-27 43-26 18 1 35 12 50 33 10-16 24-24 42-23 18 2 35 14 49 37 12-24 31-35 55-35 22 1 39 13 52 35 12-23 27-34 47-34 18 1 33 12 47 31 12-20 26-29 42-29 17 1 33 13 47 36 11-15 23-22 37-22 16 0 34 10 55 32'/%3E%3C/g%3E%3C/svg%3E"); + mask-image: linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.7) 32%, rgba(0,0,0,1) 100%); +} + +html[lang="ar"] .backdrop-books, +html[dir="rtl"] .backdrop-books { + opacity: 0.3; + background-position: right bottom, left 6% bottom 1rem; +} + +html[lang="ar"] .backdrop-scripture, +html[dir="rtl"] .backdrop-scripture { + opacity: 0.2; + transform: scaleX(-1); +} + +.backdrop-column { + width: 13rem; + height: 42rem; + border-radius: 999px; + background: linear-gradient(180deg, rgba(255,255,255,0.12), rgba(255,255,255,0.02)); + border: 1px solid rgba(255,255,255,0.08); + transform: rotate(24deg); + filter: blur(0.4px); +} + +.column-left { + left: -3rem; + bottom: -18rem; +} + +.column-right { + right: -1rem; + top: 5rem; + transform: rotate(-22deg); +} + +.library-topbar, +.library-main, +.library-footer { + position: relative; + z-index: 1; +} + +.library-topbar > .container, +.library-main > .container, +.library-footer > .container { + width: min(980px, calc(100% - clamp(3rem, 9vw, 10rem))); + max-width: 980px; + margin-inline: auto; +} + +.library-nav-shell { + margin-top: 0.5rem; + border: 1px solid rgba(255,255,255,0.08); + background: rgba(16, 22, 34, 0.66); + backdrop-filter: blur(20px); + box-shadow: var(--shadow-glow); + border-radius: 999px; + padding-inline: 0.9rem; + min-height: 4.1rem; +} + +.navbar-toggler { + border-color: rgba(255,255,255,0.18); + background: rgba(255,255,255,0.06); +} + +.navbar-toggler-icon { + filter: invert(1); } .brand-mark { - width: 2.25rem; - height: 2.25rem; - border-radius: 0.75rem; + width: 2.6rem; + height: 2.6rem; + border-radius: 0.9rem; display: inline-flex; align-items: center; justify-content: center; - background: var(--accent); - color: #fff; - font-weight: 700; - letter-spacing: 0.04em; - font-size: 0.9rem; + background: linear-gradient(135deg, var(--accent), var(--accent-warm)); + color: #07111b; + font-weight: 800; + letter-spacing: 0.08em; + font-size: 0.82rem; + box-shadow: 0 12px 28px rgba(81, 208, 255, 0.25); + flex-shrink: 0; +} + +.brand-logo { + width: 2.9rem; + height: 2.9rem; + border-radius: 0.95rem; + object-fit: cover; + flex-shrink: 0; + border: 1px solid rgba(255,255,255,0.16); + background: rgba(255,255,255,0.96); + box-shadow: 0 16px 36px rgba(5, 10, 20, 0.2); +} + +.brand-lockup { + min-width: 0; } .brand-title { - font-weight: 700; - color: var(--text); + font-weight: 800; + color: var(--text-on-dark); line-height: 1.1; + letter-spacing: -0.03em; +} + +.navbar small, +.footer-copy, +.footer-links, +.footer-links a, +.text-secondary { + color: var(--text-secondary) !important; } .nav-link { - color: var(--text-secondary); - font-weight: 500; - border-radius: var(--radius-sm); - padding-inline: 0.85rem !important; + color: rgba(238, 243, 251, 0.74); + font-weight: 700; + border-radius: 999px; + padding-inline: 0.95rem !important; + padding-block: 0.65rem !important; + transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease; } .nav-link.active, .nav-link:hover, .nav-link:focus { - color: var(--text); - background: var(--accent-soft); + color: var(--text-on-dark); + background: rgba(255,255,255,0.08); + transform: translateY(-1px); +} + +.topbar-lang-switch { + border-radius: 999px; + padding-inline: 1rem; + font-weight: 700; + line-height: 1.2; + border-color: rgba(255,255,255,0.16); + color: var(--text-on-dark); + background: rgba(255,255,255,0.05); +} + +.topbar-lang-switch:hover, +.topbar-lang-switch:focus { + background: linear-gradient(135deg, rgba(81, 208, 255, 0.2), rgba(255, 156, 82, 0.18)); + border-color: rgba(255,255,255,0.2); + color: #fff; +} + +.library-stage { + position: relative; + padding-top: clamp(0.85rem, 1.8vw, 1.25rem) !important; + padding-bottom: clamp(2rem, 3vw, 2.8rem) !important; } .hero-surface, .panel { background: var(--surface); - border: 1px solid var(--border); + border: 1px solid rgba(255,255,255,0.52); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); + backdrop-filter: blur(10px); } .hero-surface { - padding: clamp(1.5rem, 2vw, 2rem); + padding: clamp(1.5rem, 2.4vw, 2.1rem); } .panel { @@ -113,24 +336,173 @@ body { .section-kicker { display: inline-flex; align-items: center; - gap: 0.35rem; - color: var(--text-secondary); + gap: 0.45rem; + color: var(--accent-strong); text-transform: uppercase; - letter-spacing: 0.08em; + letter-spacing: 0.12em; font-size: 0.72rem; - font-weight: 700; + font-weight: 800; margin-bottom: 0.9rem; } .display-6, +.display-5, .h3, .h4, .h5 { - letter-spacing: -0.03em; + letter-spacing: -0.045em; +} + +.display-5 { + font-weight: 800; } .lead { - font-size: 1.04rem; + font-size: 1.06rem; +} + +.hero-surface--immersive { + position: relative; + overflow: hidden; + padding: clamp(1.4rem, 2.3vw, 2rem); + border-radius: var(--radius-xl); +} + +.hero-surface--immersive::before { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(circle at top right, rgba(81, 208, 255, 0.16), transparent 34%), + radial-gradient(circle at bottom left, rgba(255, 156, 82, 0.14), transparent 28%); + pointer-events: none; +} + +.hero-surface--immersive > * { + position: relative; + z-index: 1; +} + +.hero-copy-wrap { + max-width: 39rem; +} + +.hero-surface--immersive > .row { + --bs-gutter-x: clamp(1rem, 2vw, 1.75rem); + --bs-gutter-y: 0.85rem; +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: 0.85rem; +} + +.hero-stat-strip { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.8rem; + margin-top: 1.35rem; +} + +.hero-stat { + padding: 1rem 1.1rem; + border-radius: 1.2rem; + background: rgba(16, 22, 34, 0.88); + border: 1px solid rgba(255,255,255,0.08); + box-shadow: var(--shadow-glow); +} + +.hero-stat-value { + display: block; + color: var(--text-on-dark); + font-weight: 800; + font-size: 1.45rem; + letter-spacing: -0.05em; +} + +.hero-stat-label { + display: block; + color: rgba(238, 243, 251, 0.68); + font-size: 0.9rem; +} + +.hero-showcase { + display: grid; + gap: 1rem; +} + +.curator-card, +.spotlight-card, +.discovery-panel, +.tone-panel, +.panel-dark { + border-radius: 1.5rem; + box-shadow: var(--shadow-md); +} + +.curator-card, +.spotlight-card, +.discovery-panel, +.tone-panel { + background: var(--surface); + border: 1px solid rgba(255,255,255,0.52); +} + +.curator-card { + padding: 1.4rem; +} + +.curator-note { + color: var(--text-secondary); + margin-bottom: 1rem; +} + +.curator-signature { + display: flex; + align-items: center; + gap: 0.85rem; +} + +.signature-mark { + width: 2.8rem; + height: 2.8rem; + border-radius: 1rem; + display: inline-flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, rgba(81, 208, 255, 0.2), rgba(255, 156, 82, 0.22)); + color: var(--text); + font-weight: 800; +} + +.signature-name { + font-weight: 700; + display: block; +} + +.signature-role { + color: var(--text-secondary); + font-size: 0.92rem; +} + +.spotlight-card { + padding: 1.35rem; + background: linear-gradient(180deg, rgba(247, 239, 227, 0.98), rgba(255, 250, 244, 0.92)); +} + +.spotlight-head { + display: flex; + justify-content: space-between; + gap: 1rem; + align-items: start; + margin-bottom: 1rem; +} + +.spotlight-badge { + background: rgba(16, 151, 200, 0.12) !important; + color: var(--accent-strong) !important; + border: 1px solid rgba(16, 151, 200, 0.18); } .metric-grid { @@ -144,7 +516,8 @@ body { } .metric-card, -.recent-card { +.recent-card, +.curated-mini-card { background: var(--surface-muted); border: 1px solid var(--border); border-radius: var(--radius-md); @@ -158,18 +531,19 @@ body { } .metric-value { - font-size: 1.5rem; - font-weight: 700; + font-size: 1.55rem; + font-weight: 800; letter-spacing: -0.04em; } .metric-label { color: var(--text-secondary); - font-size: 0.9rem; + font-size: 0.92rem; } .compact-list, -.compact-list-numbered { +.compact-list-numbered, +.curated-points { color: var(--text-secondary); display: grid; gap: 0.65rem; @@ -181,215 +555,72 @@ body { } .compact-list li::marker, -.compact-list-numbered li::marker { - color: var(--text); +.compact-list-numbered li::marker, +.curated-points li::marker { + color: var(--accent-strong); } -.link-arrow, -.back-link { - color: var(--text); - font-weight: 600; - text-decoration: none; +.discovery-panel { + padding: 1.5rem; } -.link-arrow:hover, -.back-link:hover { - color: #000; -} - -.form-control, -.form-select { - border-color: var(--border-strong); - padding: 0.72rem 0.85rem; - border-radius: var(--radius-sm); - background: #fff; -} - -.form-control:focus, -.form-select:focus, -.btn:focus, -.nav-link:focus, -.btn-close:focus { - box-shadow: 0 0 0 0.2rem rgba(31, 41, 55, 0.12); - border-color: #9aa5b1; -} - -.form-text, -.text-secondary { - color: var(--text-secondary) !important; -} - -.btn { - border-radius: 0.65rem; - padding: 0.7rem 1rem; - font-weight: 600; -} - -.btn-dark { - background: var(--accent); - border-color: var(--accent); -} - -.btn-dark:hover, -.btn-dark:focus { - background: #111827; - border-color: #111827; -} - -.btn-outline-secondary { - color: var(--text); - border-color: var(--border-strong); -} - -.btn-outline-secondary:hover, -.btn-outline-secondary:focus { - background: var(--accent-soft); - color: var(--text); - border-color: var(--border-strong); -} - -.badge { - border-radius: 999px; - font-weight: 600; - letter-spacing: 0.01em; - padding: 0.55em 0.75em; -} - -.text-bg-light { - background: var(--accent-soft) !important; - color: var(--text) !important; - border: 1px solid var(--border); -} - -.empty-panel { - background: var(--surface); -} - -.empty-icon { - width: 3rem; - height: 3rem; - border-radius: 1rem; - background: var(--surface-muted); - border: 1px solid var(--border); - display: inline-flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 1.1rem; -} - -.table > :not(caption) > * > * { - padding-block: 0.95rem; - border-bottom-color: var(--border); -} - -.table thead th { - color: var(--text-secondary); - font-size: 0.8rem; - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.reader-panel { - padding-bottom: 1rem; -} - -.reader-frame-wrap { - border: 1px solid var(--border); - border-radius: var(--radius-md); - overflow: hidden; - background: #d1d5db; -} - -.reader-frame { - width: 100%; - min-height: 70vh; - border: 0; - background: #fff; -} - -.reader-lock, -.summary-box { - background: var(--surface-muted); - border: 1px solid var(--border); - border-radius: var(--radius-md); - padding: 1rem; -} - -.summary-box { - white-space: pre-line; - line-height: 1.7; -} - -.summary-box-muted { - color: var(--text-secondary); -} - -.description-stack { +.discovery-form-grid { display: grid; + grid-template-columns: minmax(0, 1.7fr) minmax(0, 0.9fr) auto; gap: 1rem; } -.recent-card { - display: block; - height: 100%; - transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; +.tone-panel { + padding: 1.5rem; + background: linear-gradient(180deg, rgba(250, 244, 236, 0.96), rgba(245, 238, 228, 0.92)); } -.recent-card:hover, -.recent-card:focus { - transform: translateY(-2px); - box-shadow: var(--shadow-md); - border-color: var(--border-strong); +.tone-swatch-row { + display: flex; + flex-wrap: wrap; + gap: 0.7rem; + margin-bottom: 1rem; } -.toast { - min-width: 280px; - border-radius: 0.85rem; - overflow: hidden; +.tone-swatch { + width: 3rem; + height: 3rem; + border-radius: 1rem; + border: 1px solid rgba(0,0,0,0.06); + box-shadow: inset 0 1px 0 rgba(255,255,255,0.35); } -.toast-stack { - z-index: 1090; +.tone-swatch.cyan { + background: linear-gradient(135deg, #7be3ff, #26a8db); } -footer a { - color: var(--text); +.tone-swatch.ivory { + background: linear-gradient(135deg, #fff9f0, #ece0cf); } -@media (max-width: 991.98px) { - .metric-grid { - grid-template-columns: 1fr; - } - - .reader-frame { - min-height: 60vh; - } +.tone-swatch.amber { + background: linear-gradient(135deg, #ffc489, #ef7d37); } -/* Flipbook Custom Styles */ -#flipbook-wrapper { - background: #333; - box-shadow: inset 0 0 30px rgba(0,0,0,0.6); +.catalog-section-head { + display: flex; + justify-content: space-between; + gap: 1rem; + align-items: end; + margin-bottom: 1.25rem; } -#flipbook .page { - background-color: #fff; - /* Soft border for realism */ - border-right: 1px solid #ddd; +.catalog-shell { + position: relative; } -#flipbook-toolbar button { - transition: all 0.2s ease; -} - -#flipbook-toolbar button:hover { - transform: scale(1.1); - color: var(--accent); -} - -#flipbook-toolbar button:active { - transform: scale(0.95); +.catalog-shell::after { + content: ""; + position: absolute; + inset: auto 0 -1.1rem 0; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.34), transparent); + opacity: 0.6; } .catalog-card { @@ -397,6 +628,14 @@ footer a { grid-template-columns: 128px minmax(0, 1fr); gap: 1.25rem; align-items: stretch; + transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease; +} + +.catalog-card:hover, +.catalog-card:focus-within { + transform: translateY(-4px); + box-shadow: var(--shadow-md); + border-color: rgba(16, 151, 200, 0.18); } .catalog-card-media { @@ -409,7 +648,7 @@ footer a { height: 176px; object-fit: cover; border-radius: var(--radius-md); - border: 1px solid var(--border); + border: 1px solid rgba(16, 22, 34, 0.08); background: var(--surface-muted); box-shadow: var(--shadow-sm); } @@ -420,9 +659,9 @@ footer a { justify-content: center; color: var(--text-secondary); font-size: 0.9rem; - font-weight: 600; + font-weight: 700; text-transform: uppercase; - letter-spacing: 0.06em; + letter-spacing: 0.08em; } .catalog-card-body { @@ -460,10 +699,354 @@ footer a { .catalog-card-label { min-width: 3.75rem; color: var(--text); - font-weight: 600; + font-weight: 700; +} + +.recent-card { + display: block; + height: 100%; + transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; +} + +.recent-card:hover, +.recent-card:focus { + transform: translateY(-2px); + box-shadow: var(--shadow-md); + border-color: var(--border-strong); +} + +.link-arrow, +.back-link { + color: var(--text); + font-weight: 700; + text-decoration: none; +} + +.link-arrow:hover, +.back-link:hover { + color: var(--accent-strong); +} + +.form-control, +.form-select { + border-color: rgba(53, 71, 95, 0.18); + padding: 0.78rem 0.9rem; + border-radius: 1rem; + background: rgba(255,255,255,0.88); +} + +.form-control:focus, +.form-select:focus, +.btn:focus, +.nav-link:focus, +.btn-close:focus { + box-shadow: 0 0 0 0.2rem rgba(81, 208, 255, 0.16); + border-color: rgba(16, 151, 200, 0.34); +} + +.btn { + border-radius: 999px; + padding: 0.8rem 1.15rem; + font-weight: 700; + letter-spacing: -0.01em; +} + +.btn-dark { + color: #08131d; + background: linear-gradient(135deg, var(--accent), var(--accent-warm)); + border: none; + box-shadow: 0 16px 36px rgba(81, 208, 255, 0.18); +} + +.btn-dark:hover, +.btn-dark:focus { + color: #08131d; + background: linear-gradient(135deg, #74ddff, #ffb67f); +} + +.btn-outline-secondary, +.btn-outline-dark { + color: var(--text); + border-color: rgba(53, 71, 95, 0.18); + background: rgba(255,255,255,0.35); +} + +.btn-outline-secondary:hover, +.btn-outline-secondary:focus, +.btn-outline-dark:hover, +.btn-outline-dark:focus { + background: rgba(81, 208, 255, 0.10); + color: var(--text); + border-color: rgba(16, 151, 200, 0.20); +} + +.badge { + border-radius: 999px; + font-weight: 700; + letter-spacing: 0.01em; + padding: 0.58em 0.82em; +} + +.text-bg-light { + background: var(--accent-soft) !important; + color: var(--accent-strong) !important; + border: 1px solid rgba(16, 151, 200, 0.18); +} + +.empty-panel { + background: rgba(248, 242, 232, 0.72); +} + +.empty-icon { + width: 3rem; + height: 3rem; + border-radius: 1rem; + background: linear-gradient(135deg, rgba(81, 208, 255, 0.18), rgba(255, 156, 82, 0.20)); + border: 1px solid rgba(16, 151, 200, 0.12); + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 800; + font-size: 1.1rem; +} + +.table > :not(caption) > * > * { + padding-block: 0.95rem; + border-bottom-color: rgba(53, 71, 95, 0.14); +} + +.table thead th { + color: var(--text-secondary); + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +.reader-panel { + padding-bottom: 1rem; +} + +.reader-frame-wrap { + border: 1px solid rgba(53, 71, 95, 0.14); + border-radius: var(--radius-md); + overflow: hidden; + background: #d1d5db; +} + +.reader-frame { + width: 100%; + min-height: 70vh; + border: 0; + background: #fff; +} + +.reader-lock, +.summary-box { + background: rgba(250, 244, 236, 0.88); + border: 1px solid rgba(53, 71, 95, 0.12); + border-radius: var(--radius-md); + padding: 1rem; +} + +.summary-box { + white-space: pre-line; + line-height: 1.7; +} + +.summary-box-muted { + color: var(--text-secondary); +} + +.description-stack { + display: grid; + gap: 1rem; +} + +.toast { + min-width: 280px; + border-radius: 0.95rem; + overflow: hidden; +} + +.toast-stack { + z-index: 1090; +} + +.library-footer { + padding-bottom: 2rem; +} + +.library-footer-panel { + background: rgba(16, 22, 34, 0.72); + color: var(--text-on-dark); + border-radius: 1.6rem; + border: 1px solid rgba(255,255,255,0.08); + backdrop-filter: blur(16px); + box-shadow: var(--shadow-glow); +} + +.footer-brand-logo { + width: 3.35rem; + height: 3.35rem; +} + +.footer-section-title { + color: rgba(238, 243, 251, 0.88); + font-weight: 700; + letter-spacing: 0.02em; +} + +.footer-description { + max-width: 34rem; +} + +.footer-detail-list { + display: grid; + gap: 0.65rem; +} + +.footer-detail-list div { + display: flex; + align-items: flex-start; + gap: 0.7rem; +} + +.footer-detail-list i { + color: var(--accent); + margin-top: 0.1rem; +} + +.footer-socials { + display: flex; + flex-wrap: wrap; + gap: 0.65rem; +} + +.footer-socials a { + width: 2.35rem; + height: 2.35rem; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 999px; + background: rgba(255,255,255,0.08); + border: 1px solid rgba(255,255,255,0.08); + transition: transform 0.2s ease, background-color 0.2s ease, color 0.2s ease; +} + +.footer-socials a:hover { + transform: translateY(-1px); + background: rgba(81, 208, 255, 0.14); +} + +.footer-meta-note { + color: rgba(238, 243, 251, 0.68); + font-size: 0.85rem; +} + +.footer-title, +footer a { + color: var(--text-on-dark); +} + +.footer-links a:hover { + color: var(--accent); +} + +.profile-hero { + background: linear-gradient(135deg, rgba(255,255,255,0.98), rgba(238,241,244,0.96)); +} + +.profile-metric-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.metric-value-small { + font-size: 1rem; + line-height: 1.5; + letter-spacing: normal; +} + +.profile-summary-list dt, +.profile-summary-list dd { + line-height: 1.6; +} + +.history-card { + display: block; +} + +.history-card-meta { + display: grid; + gap: 0.35rem; +} + +#flipbook-wrapper { + background: #333; + box-shadow: inset 0 0 30px rgba(0,0,0,0.6); +} + +#flipbook .page { + background-color: #fff; + border-right: 1px solid #ddd; +} + +#flipbook-toolbar button { + transition: all 0.2s ease; +} + +#flipbook-toolbar button:hover { + transform: scale(1.1); + color: var(--accent-strong); +} + +#flipbook-toolbar button:active { + transform: scale(0.95); +} + +@media (max-width: 991.98px) { + .library-nav-shell { + border-radius: 1.5rem; + padding-block: 0.5rem; + } + + .backdrop-books { + inset-inline: -4rem; + bottom: 3rem; + opacity: 0.18; + background-size: 30rem auto, 24rem auto; + } + + .backdrop-scripture { + inset-inline: 0; + opacity: 0.1; + background-size: 24rem auto; + } + + .hero-stat-strip, + .metric-grid, + .profile-metric-grid, + .discovery-form-grid { + grid-template-columns: 1fr; + } + + .spotlight-head, + .catalog-section-head { + flex-direction: column; + align-items: start; + } + + .column-right { + top: 18rem; + } } @media (max-width: 767.98px) { + .backdrop-books, + .backdrop-scripture { + display: none; + } + .catalog-card { grid-template-columns: 1fr; } @@ -475,18 +1058,20 @@ footer a { .catalog-card-body { text-align: start; } -} -.topbar-lang-switch { - border-radius: 999px; - padding-inline: 0.9rem; - font-weight: 700; - line-height: 1.2; -} + .hero-surface--immersive { + padding: 1.4rem; + } -.topbar-lang-switch:hover, -.topbar-lang-switch:focus { - background: var(--accent); - border-color: var(--accent); - color: #fff; + .brand-title { + font-size: 0.98rem; + } + + .footer-brand { + flex-direction: column; + } + + .reader-frame { + min-height: 60vh; + } } diff --git a/db/migrations/007_create_reader_activity.sql b/db/migrations/007_create_reader_activity.sql new file mode 100644 index 0000000..62e6428 --- /dev/null +++ b/db/migrations/007_create_reader_activity.sql @@ -0,0 +1,47 @@ +CREATE TABLE IF NOT EXISTS library_readers ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + reader_token CHAR(64) NOT NULL, + preferred_language VARCHAR(12) NOT NULL DEFAULT 'en', + last_path VARCHAR(255) DEFAULT NULL, + user_agent VARCHAR(255) DEFAULT NULL, + first_seen_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_seen_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id), + UNIQUE KEY uniq_library_reader_token (reader_token), + KEY idx_library_readers_last_seen (last_seen_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS library_reader_visits ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + reader_id BIGINT UNSIGNED NOT NULL, + session_key VARCHAR(64) NOT NULL, + entry_path VARCHAR(255) DEFAULT NULL, + started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_activity_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + page_views INT UNSIGNED NOT NULL DEFAULT 1, + PRIMARY KEY (id), + UNIQUE KEY uniq_library_reader_visit (reader_id, session_key), + KEY idx_library_reader_visits_started (reader_id, started_at), + CONSTRAINT fk_library_reader_visits_reader FOREIGN KEY (reader_id) REFERENCES library_readers(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS library_reader_activities ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + reader_id BIGINT UNSIGNED NOT NULL, + visit_id BIGINT UNSIGNED DEFAULT NULL, + document_id INT UNSIGNED DEFAULT NULL, + event_type VARCHAR(50) NOT NULL, + page_path VARCHAR(255) DEFAULT NULL, + context VARCHAR(20) NOT NULL DEFAULT 'public', + meta_json TEXT DEFAULT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id), + KEY idx_library_reader_activities_reader_created (reader_id, created_at), + KEY idx_library_reader_activities_visit (visit_id), + KEY idx_library_reader_activities_document (document_id), + CONSTRAINT fk_library_reader_activities_reader FOREIGN KEY (reader_id) REFERENCES library_readers(id) ON DELETE CASCADE, + CONSTRAINT fk_library_reader_activities_visit FOREIGN KEY (visit_id) REFERENCES library_reader_visits(id) ON DELETE SET NULL, + CONSTRAINT fk_library_reader_activities_document FOREIGN KEY (document_id) REFERENCES library_documents(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/db/migrations/008_create_library_settings.sql b/db/migrations/008_create_library_settings.sql new file mode 100644 index 0000000..949bc56 --- /dev/null +++ b/db/migrations/008_create_library_settings.sql @@ -0,0 +1,29 @@ +CREATE TABLE IF NOT EXISTS library_settings ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT, + library_name_en VARCHAR(255) DEFAULT NULL, + library_name_ar VARCHAR(255) DEFAULT NULL, + short_name VARCHAR(32) DEFAULT NULL, + tagline_en VARCHAR(255) DEFAULT NULL, + tagline_ar VARCHAR(255) DEFAULT NULL, + description_en TEXT DEFAULT NULL, + description_ar TEXT DEFAULT NULL, + contact_email VARCHAR(255) DEFAULT NULL, + contact_phone VARCHAR(80) DEFAULT NULL, + whatsapp_number VARCHAR(80) DEFAULT NULL, + website_url VARCHAR(255) DEFAULT NULL, + address_en VARCHAR(255) DEFAULT NULL, + address_ar VARCHAR(255) DEFAULT NULL, + opening_hours_en VARCHAR(255) DEFAULT NULL, + opening_hours_ar VARCHAR(255) DEFAULT NULL, + facebook_url VARCHAR(255) DEFAULT NULL, + instagram_url VARCHAR(255) DEFAULT NULL, + x_url VARCHAR(255) DEFAULT NULL, + youtube_url VARCHAR(255) DEFAULT NULL, + logo_path VARCHAR(255) DEFAULT NULL, + favicon_path VARCHAR(255) DEFAULT NULL, + copyright_text_en VARCHAR(255) DEFAULT NULL, + copyright_text_ar VARCHAR(255) DEFAULT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/document.php b/document.php index 23b2f9f..754d32c 100644 --- a/document.php +++ b/document.php @@ -126,6 +126,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'gener if ($context !== 'admin') { library_increment_views((int) $document['id']); $document = library_fetch_document((int) $document['id'], true) ?: $document; + library_track_request('document_viewed', (int) $document['id'], [ + 'document_language' => (string) ($document['document_language'] ?? ''), + ]); } $selectedTitle = library_localized_document_title($document, $lang, $pageCopy['detail_fallback']); diff --git a/includes/admin_layout.php b/includes/admin_layout.php index d08e90e..b4ec5d4 100644 --- a/includes/admin_layout.php +++ b/includes/admin_layout.php @@ -8,8 +8,13 @@ function admin_render_header(string $title, string $activePage = 'dashboard'): v $lang = library_get_language(); $dir = $lang === 'ar' ? 'rtl' : 'ltr'; $isRtl = $lang === 'ar'; - - // Get flashes and clear them + $profile = library_get_profile(); + $brandName = library_profile_name($lang); + $brandTagline = library_profile_tagline($lang); + $brandShortName = trim((string) ($profile['short_name'] ?? '')) ?: 'NL'; + $logoPath = trim((string) ($profile['logo_path'] ?? '')); + $faviconPath = trim((string) ($profile['favicon_path'] ?? '')); + $flashes = []; if (isset($_SESSION['library_flash'])) { $flashes = $_SESSION['library_flash']; @@ -22,6 +27,10 @@ function admin_render_header(string $title, string $activePage = 'dashboard'): v <?= h($title) ?> · <?= library_trans('admin_panel') ?> + + + + @@ -30,7 +39,7 @@ function admin_render_header(string $title, string $activePage = 'dashboard'): v