BRCGSV9 / BRCGSv9app.html
MMOON's picture
Upload 3 files
3fa1190 verified
Raw
History Blame Contribute Delete
61.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BRCGS V9 Navigator — Food Safety Issue 9</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
<style>
:root {
--brand: #008573;
--brand-dark: #005c4f;
--brand-light: #e0f2f1;
--accent: #f59e0b;
--accent-dark: #b45309;
--accent-bg: #fffde7;
--accent-bg-header: #fef3c7;
--accent-bg-hover: #fde68a;
--fav: #f59e0b;
--bg-base: #f8fafc;
--bg-surface: #ffffff;
--bg-muted: #f1f5f9;
--bg-sidebar: #ffffff;
--text-primary: #1e293b;
--text-secondary: #475569;
--text-tertiary: #64748b;
--border: #e2e8f0;
--sidebar-width: 280px;
--radius: 8px;
--shadow: 0 1px 3px rgba(0,0,0,0.08);
--shadow-md: 0 4px 12px rgba(0,0,0,0.1);
--highlight-bg: #fef08a;
--highlight-text: #713f12;
}
[data-theme="dark"] {
--bg-base: #0f172a;
--bg-surface: #1e293b;
--bg-muted: #334155;
--bg-sidebar: #0f172a;
--text-primary: #f1f5f9;
--text-secondary: #cbd5e1;
--text-tertiary: #94a3b8;
--border: #334155;
--brand-light: #134e4a;
--accent-bg: #292524;
--accent-bg-header: #1c1917;
--accent-bg-hover: #3b2a1a;
--highlight-bg: #713f12;
--highlight-text: #fef08a;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; -webkit-font-smoothing: antialiased; scroll-behavior: smooth; }
body { font-family: 'Inter', sans-serif; background: var(--bg-base); color: var(--text-primary); height: 100vh; overflow: hidden; }
button { font-family: inherit; cursor: pointer; border: none; background: none; }
a { color: var(--brand); text-decoration: none; }
h1, h2, h3 { font-weight: 700; }
.app-layout { display: flex; height: 100vh; }
.sidebar { width: var(--sidebar-width); background: var(--bg-sidebar); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow: hidden; flex-shrink: 0; }
.sidebar__brand { padding: 18px 16px; background: linear-gradient(135deg, var(--brand-dark), var(--brand)); color: white; cursor: pointer; user-select: none; }
.sidebar__brand:hover { opacity: 0.9; }
.sidebar__brand h1 { font-size: 14px; font-weight: 700; }
.sidebar__brand p { font-size: 10px; opacity: 0.8; margin-top: 3px; font-weight: 500; }
.sidebar__nav { flex: 1; overflow-y: auto; padding: 8px 0; }
.sidebar__footer { padding: 12px 16px; border-top: 1px solid var(--border); }
.sidebar__read-label { font-size: 10px; font-weight: 600; text-transform: uppercase; color: var(--text-tertiary); letter-spacing: 0.06em; margin-bottom: 6px; }
.sidebar__read-bar { height: 4px; background: var(--border); border-radius: 2px; overflow: hidden; }
.sidebar__read-fill { height: 100%; background: var(--brand); border-radius: 2px; transition: width 0.4s ease; }
.sidebar__read-text { font-size: 10px; color: var(--text-tertiary); margin-top: 4px; text-align: center; }
.nav-group__header { padding: 14px 16px 6px; font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--text-tertiary); letter-spacing: 0.06em; }
.nav-item { padding: 9px 16px; cursor: pointer; display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text-secondary); border-left: 3px solid transparent; transition: all 0.15s; position: relative; }
.nav-item:hover { background: var(--bg-muted); color: var(--text-primary); }
.nav-item.active { background: var(--brand); color: white; border-left-color: var(--brand-dark); }
.nav-item .material-icons-round { font-size: 17px; opacity: 0.65; flex-shrink: 0; }
.nav-item.active .material-icons-round { opacity: 1; }
.nav-item__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-badge { margin-left: auto; background: var(--bg-muted); color: var(--text-tertiary); font-size: 10px; font-weight: 700; padding: 2px 6px; border-radius: 10px; flex-shrink: 0; }
.nav-item.active .nav-badge { background: rgba(255,255,255,0.22); color: rgba(255,255,255,0.9); }
.nav-item--visited::after { content: ''; position: absolute; right: 38px; top: 50%; transform: translateY(-50%); width: 6px; height: 6px; background: var(--brand); border-radius: 50%; }
.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
.header { height: 56px; background: var(--bg-surface); border-bottom: 1px solid var(--border); display: flex; align-items: center; padding: 0 16px; gap: 10px; position: relative; z-index: 50; }
.header__btn { padding: 7px; border-radius: var(--radius); color: var(--text-secondary); transition: all 0.15s; display: flex; align-items: center; }
.header__btn:hover { background: var(--bg-muted); color: var(--text-primary); }
.header__search { flex: 1; max-width: 360px; position: relative; }
.header__search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); font-size: 17px; pointer-events: none; }
.header__search-input { width: 100%; padding: 8px 32px 8px 34px; border-radius: var(--radius); border: 1px solid var(--border); background: var(--bg-muted); font-size: 13px; color: var(--text-primary); }
.header__search-input:focus { outline: none; border-color: var(--brand); background: var(--bg-surface); box-shadow: 0 0 0 3px rgba(0,133,115,0.1); }
.header__search-clear { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); font-size: 16px; cursor: pointer; display: none; }
.header__search-clear.visible { display: block; }
.header__spacer { flex: 1; }
.header__actions { display: flex; align-items: center; gap: 2px; }
.search-panel { position: absolute; top: 100%; left: 0; right: 0; background: var(--bg-surface); border-bottom: 2px solid var(--brand); z-index: 250; max-height: 380px; overflow-y: auto; box-shadow: var(--shadow-md); display: none; }
.search-panel.open { display: block; }
.search-panel__header { padding: 8px 16px; font-size: 11px; font-weight: 600; color: var(--text-tertiary); text-transform: uppercase; background: var(--bg-muted); border-bottom: 1px solid var(--border); }
.search-result { padding: 11px 16px; cursor: pointer; border-bottom: 1px solid var(--border); display: flex; align-items: flex-start; gap: 12px; }
.search-result:hover { background: var(--bg-muted); }
.search-result__id { background: var(--brand); color: white; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; font-family: 'JetBrains Mono', monospace; }
.search-result__section { font-size: 10px; color: var(--text-tertiary); font-weight: 600; text-transform: uppercase; }
.search-result__text { font-size: 13px; color: var(--text-secondary); }
.content-wrapper { flex: 1; overflow: hidden; position: relative; }
.content { height: 100%; overflow-y: auto; padding: 28px 24px; }
.content__inner { max-width: 860px; margin: 0 auto; }
.home-hero { background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%); color: white; border-radius: 12px; padding: 28px 32px; margin-bottom: 28px; }
.home-hero h2 { font-size: 20px; font-weight: 700; margin-bottom: 8px; }
.home-hero p { font-size: 14px; opacity: 0.85; line-height: 1.65; max-width: 500px; }
.home-hero__meta { display: flex; gap: 10px; margin-top: 18px; flex-wrap: wrap; }
.home-hero__badge { background: rgba(255,255,255,0.18); border-radius: 6px; padding: 5px 11px; font-size: 12px; font-weight: 600; }
.home-sections-title { font-size: 11px; font-weight: 700; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 12px; }
.home-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
.breadcrumb { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 10px; cursor: pointer; padding: 4px 8px; border-radius: 6px; }
.breadcrumb:hover { color: var(--brand); background: var(--brand-light); }
.section-header { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 2px solid var(--border); }
.section-header__title { font-size: 22px; font-weight: 700; color: var(--text-primary); }
.section-header__meta { font-size: 12px; color: var(--text-tertiary); margin-top: 6px; display: flex; align-items: center; gap: 8px; }
.section-header__meta-dot { width: 3px; height: 3px; background: var(--border); border-radius: 50%; }
.section-header__actions { display: flex; gap: 6px; }
.btn-ghost { padding: 6px 12px; border-radius: var(--radius); font-size: 12px; font-weight: 600; color: var(--text-secondary); border: 1px solid var(--border); background: var(--bg-surface); display: flex; align-items: center; gap: 5px; }
.btn-ghost:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-light); }
.soi-card { background: var(--brand-light); border-left: 4px solid var(--brand); border-radius: var(--radius); padding: 16px 20px; margin-bottom: 20px; }
.soi-card__label { font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--brand); margin-bottom: 8px; }
.soi-card__text { font-size: 14px; color: var(--text-secondary); line-height: 1.8; }
.clause-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 22px; margin-bottom: 12px; box-shadow: var(--shadow); }
.clause-card:hover { border-color: var(--brand); box-shadow: 0 2px 8px rgba(0,133,115,0.12); }
.clause-card__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.clause-card__id { display: inline-flex; align-items: center; background: var(--brand); color: white; padding: 4px 10px; border-radius: 4px; font-size: 11.5px; font-weight: 600; font-family: 'JetBrains Mono', monospace; }
.clause-card__actions { display: flex; align-items: center; gap: 4px; }
.clause-fav-btn { padding: 6px; border-radius: 6px; color: var(--text-tertiary); }
.clause-fav-btn:hover { color: var(--fav); background: var(--accent-bg); }
.clause-fav-btn.active { color: var(--fav); }
.clause-anchor-btn { padding: 6px; border-radius: 6px; color: var(--text-tertiary); }
.clause-anchor-btn:hover { color: var(--brand); background: var(--bg-muted); }
.clause-card__text { font-size: 14px; font-weight: 500; line-height: 1.8; color: var(--text-primary); }
.interp-card { background: var(--accent-bg); border: 1px solid rgba(245,158,11,0.35); border-left: 4px solid var(--accent); border-radius: var(--radius); margin-top: 14px; overflow: hidden; }
.interp-card__header { display: flex; align-items: center; gap: 10px; padding: 12px 16px; background: var(--accent-bg-header); cursor: pointer; }
.interp-card__header:hover { background: var(--accent-bg-hover); }
.interp-card__icon { color: var(--accent-dark); font-size: 17px; }
.interp-card__label { font-size: 10.5px; font-weight: 700; text-transform: uppercase; color: var(--accent-dark); flex: 1; letter-spacing: 0.06em; }
.interp-card__toggle { color: var(--accent-dark); font-size: 20px; transition: transform 0.3s ease; }
.interp-card__body { max-height: 0; overflow: hidden; transition: max-height 0.35s ease-out; }
.interp-card.expanded .interp-card__body { max-height: 4000px; }
.interp-card.expanded .interp-card__toggle { transform: rotate(180deg); }
.interp-card__content { padding: 16px 18px; font-size: 14px; color: var(--text-primary); line-height: 1.9; }
.interp-card__content p { margin-bottom: 12px; }
.interp-card__content .bullet-item { display: block; margin: 6px 0 6px 16px; padding: 6px 10px 6px 12px; border-left: 3px solid var(--accent); color: var(--text-secondary); font-size: 13.5px; }
[data-theme="dark"] .interp-card { background: #1e1a17; border-color: #b45309; }
[data-theme="dark"] .interp-card__header { background: #1c1917; }
[data-theme="dark"] .interp-card__icon, [data-theme="dark"] .interp-card__label, [data-theme="dark"] .interp-card__toggle { color: #fbbf24; }
.toast { position: fixed; bottom: 24px; right: 24px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 20px; box-shadow: var(--shadow-md); display: flex; align-items: center; gap: 10px; z-index: 1000; transform: translateY(100px); opacity: 0; transition: transform 0.3s, opacity 0.3s; font-size: 13px; font-weight: 500; }
.toast.show { transform: translateY(0); opacity: 1; }
.loader { position: fixed; inset: 0; background: var(--bg-surface); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 9999; gap: 14px; }
.loader.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
.loader__spinner { width: 34px; height: 34px; border: 3px solid var(--bg-muted); border-top-color: var(--brand); border-radius: 50%; animation: spin 0.75s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media print {
.sidebar, .header, .search-panel, .toast, .loader, .clause-card__actions, .section-header__actions, .breadcrumb { display: none !important; }
html, body { overflow: visible; height: auto; background: white; }
.app-layout { display: block; }
.content-wrapper, .content { overflow: visible; height: auto; }
.interp-card__body { max-height: none !important; }
.clause-card { break-inside: avoid; box-shadow: none; border: 1px solid #d1d5db; }
.content__inner { max-width: 100%; }
}
@media (max-width: 768px) {
.sidebar { position: fixed; left: -100%; z-index: 1000; height: 100%; }
.sidebar.open { left: 0; box-shadow: var(--shadow-md); }
.sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 999; }
.sidebar-overlay.visible { display: block; }
.header { padding: 0 12px; gap: 8px; }
.header__search { max-width: 180px; }
.content { padding: 20px 16px; }
.home-grid { grid-template-columns: 1fr 1fr; gap: 8px; }
.section-header__title { font-size: 18px; }
.section-header__actions { display: none; }
.home-hero { padding: 22px 18px; }
.home-hero h2 { font-size: 17px; }
}
@media (max-width: 420px) {
.home-grid { grid-template-columns: 1fr; }
}
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* ===== DARK THEME & RESET ===== */
[data-theme="dark"] {
--bg-base: #0f172a;
--bg-surface: #1e293b;
--bg-muted: #334155;
--bg-sidebar: #0f172a;
--text-primary: #f1f5f9;
--text-secondary: #cbd5e1;
--text-tertiary: #94a3b8;
--border: #334155;
--brand-light: #134e4a;
--accent-bg: #292524;
--accent-bg-header: #1c1917;
--accent-bg-hover: #3b2a1a;
--highlight-bg: #713f12;
--highlight-text: #fef08a;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; -webkit-font-smoothing: antialiased; scroll-behavior: smooth; }
body { font-family: 'Inter', sans-serif; background: var(--bg-base); color: var(--text-primary); height: 100vh; overflow: hidden; }
button { font-family: inherit; cursor: pointer; border: none; background: none; }
a { color: var(--brand); text-decoration: none; }
h1, h2, h3 { font-weight: 700; }
.app-layout { display: flex; height: 100vh; }
/* ===== SIDEBAR ===== */
.sidebar { width: var(--sidebar-width); background: var(--bg-sidebar); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow: hidden; flex-shrink: 0; }
.sidebar__brand { padding: 18px 16px; background: linear-gradient(135deg, var(--brand-dark), var(--brand)); color: white; cursor: pointer; user-select: none; transition: opacity 0.15s, filter 0.15s; }
.sidebar__brand:hover { opacity: 0.9; filter: brightness(1.05); }
.sidebar__brand h1 { font-size: 14px; font-weight: 700; letter-spacing: -0.01em; }
.sidebar__brand p { font-size: 10px; opacity: 0.8; margin-top: 3px; font-weight: 500; }
.sidebar__nav { flex: 1; overflow-y: auto; padding: 8px 0; }
.sidebar__footer { padding: 12px 16px; border-top: 1px solid var(--border); }
.sidebar__read-label { font-size: 10px; font-weight: 600; text-transform: uppercase; color: var(--text-tertiary); letter-spacing: 0.06em; margin-bottom: 6px; }
.sidebar__read-bar { height: 4px; background: var(--border); border-radius: 2px; overflow: hidden; }
.sidebar__read-fill { height: 100%; background: var(--brand); border-radius: 2px; transition: width 0.4s ease; }
.sidebar__read-text { font-size: 10px; color: var(--text-tertiary); margin-top: 4px; text-align: center; }
.nav-group__header { padding: 14px 16px 6px; font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--text-tertiary); letter-spacing: 0.06em; }
.nav-item { padding: 9px 16px; cursor: pointer; display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text-secondary); border-left: 3px solid transparent; transition: all 0.15s; position: relative; }
.nav-item:hover { background: var(--bg-muted); color: var(--text-primary); }
.nav-item.active { background: var(--brand); color: white; border-left-color: var(--brand-dark); }
.nav-item .material-icons-round { font-size: 17px; opacity: 0.65; flex-shrink: 0; }
.nav-item.active .material-icons-round { opacity: 1; }
.nav-item__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; word-break: break-word; }
.nav-badge { margin-left: auto; background: var(--bg-muted); color: var(--text-tertiary); font-size: 10px; font-weight: 700; padding: 2px 6px; border-radius: 10px; flex-shrink: 0; line-height: 1.4; white-space: nowrap; }
.nav-item.active .nav-badge { background: rgba(255,255,255,0.22); color: rgba(255,255,255,0.9); }
.nav-item--visited::after { content: ''; position: absolute; right: 38px; top: 50%; transform: translateY(-50%); width: 6px; height: 6px; background: var(--brand); border-radius: 50%; }
/* ===== VIEW DENSITY ===== */
.view-density-compact .clause-card { padding: 12px 16px; margin-bottom: 8px; }
.view-density-compact .clause-card__text { font-size: 13px; line-height: 1.6; }
.view-density-compact .interp-card { margin-top: 8px; }
.view-density-compact .interp-card__content { font-size: 12.5px; padding: 10px 12px; }
.view-density-compact .interp-card__content p { margin-bottom: 6px; }
.view-density-compact .interp-card__header { padding: 7px 12px; }
.view-density-compact .interp-card__label { font-size: 10px; }
/* ===== HEADER ===== */
.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
.header { height: 56px; min-height: 56px; background: var(--bg-surface); border-bottom: 1px solid var(--border); display: flex; align-items: center; padding: 0 16px; gap: 10px; position: relative; z-index: 50; }
.header__btn { padding: 7px; border-radius: var(--radius); color: var(--text-secondary); transition: all 0.15s; display: flex; align-items: center; }
.header__btn:hover { background: var(--bg-muted); color: var(--text-primary); }
.header__btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.header__search { flex: 1; max-width: 360px; position: relative; }
.header__search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); font-size: 17px; pointer-events: none; }
.header__search-input { width: 100%; padding: 8px 32px 8px 34px; border-radius: var(--radius); border: 1px solid var(--border); background: var(--bg-muted); font-size: 13px; color: var(--text-primary); transition: all 0.15s; }
.header__search-input:focus { outline: none; border-color: var(--brand); background: var(--bg-surface); box-shadow: 0 0 0 3px rgba(0,133,115,0.1); }
.header__search-input::placeholder { color: var(--text-tertiary); }
.header__search-clear { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); font-size: 16px; cursor: pointer; display: none; padding: 2px; border-radius: 4px; transition: color 0.15s; }
.header__search-clear:hover { color: var(--text-primary); }
.header__search-clear.visible { display: block; }
.header__spacer { flex: 1; }
.header__actions { display: flex; align-items: center; gap: 2px; }
/* ===== SEARCH DROPDOWN ===== */
.search-panel { position: absolute; top: 100%; left: 0; right: 0; background: var(--bg-surface); border-bottom: 2px solid var(--brand); border-left: 1px solid var(--border); border-right: 1px solid var(--border); z-index: 250; max-height: 380px; overflow-y: auto; box-shadow: var(--shadow-md); display: none; border-radius: 0 0 var(--radius) var(--radius); }
.search-panel.open { display: block; }
.search-panel__header { padding: 8px 16px; font-size: 11px; font-weight: 600; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid var(--border); background: var(--bg-muted); }
.search-result { padding: 11px 16px; cursor: pointer; border-bottom: 1px solid var(--border); display: flex; align-items: flex-start; gap: 12px; transition: background 0.1s; }
.search-result:hover { background: var(--bg-muted); }
.search-result:last-child { border-bottom: none; }
.search-result__id { background: var(--brand); color: white; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; font-family: 'JetBrains Mono', monospace; flex-shrink: 0; margin-top: 1px; }
.search-result__body { min-width: 0; }
.search-result__section { font-size: 10px; color: var(--text-tertiary); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 2px; }
.search-result__text { font-size: 13px; color: var(--text-secondary); line-height: 1.4; }
.search-result__text mark { background: var(--highlight-bg); color: var(--highlight-text); border-radius: 2px; padding: 0 1px; font-style: normal; }
.search-empty { padding: 28px 16px; text-align: center; color: var(--text-tertiary); font-size: 13px; }
.search-more { padding: 8px 16px; text-align: center; font-size: 12px; color: var(--text-tertiary); background: var(--bg-muted); border-top: 1px solid var(--border); }
/* ===== CONTENT ===== */
.content-wrapper { flex: 1; overflow: hidden; position: relative; }
.content { height: 100%; overflow-y: auto; padding: 28px 24px; }
.content__inner { max-width: 860px; margin: 0 auto; }
/* ===== HOME PAGE ===== */
.home-hero { background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%); color: white; border-radius: 12px; padding: 28px 32px; margin-bottom: 28px; }
.home-hero h2 { font-size: 20px; font-weight: 700; margin-bottom: 8px; letter-spacing: -0.02em; }
.home-hero p { font-size: 14px; opacity: 0.85; line-height: 1.65; max-width: 500px; }
.home-hero__meta { display: flex; gap: 10px; margin-top: 18px; flex-wrap: wrap; }
.home-hero__badge { background: rgba(255,255,255,0.18); border-radius: 6px; padding: 5px 11px; font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 5px; }
.home-hero__badge .material-icons-round { font-size: 13px; }
/* ===== HOME CARDS ===== */
.home-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; cursor: pointer; transition: all 0.15s; position: relative; overflow: hidden; }
.home-card:hover { border-color: var(--brand); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.home-card:focus { outline: 2px solid var(--brand); outline-offset: 2px; }
.home-card__num { font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--brand); letter-spacing: 0.08em; margin-bottom: 8px; }
.home-card__title { font-size: 16px; font-weight: 600; color: var(--text-primary); line-height: 1.4; margin-bottom: 12px; }
.home-card__footer { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-tertiary); }
.home-card__count { display: flex; align-items: center; gap: 4px; }
.home-card__arrow { font-size: 16px; color: var(--brand); transition: transform 0.15s; }
.home-card:hover .home-card__arrow { transform: translateX(4px); }
/* ===== HOME GRID ===== */
.home-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
/* ===== BREADCRUMB ===== */
.breadcrumb { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; text-transform: uppercase; color: var(--text-tertiary); letter-spacing: 0.05em; margin-bottom: 10px; cursor: pointer; padding: 4px 8px 4px 2px; border-radius: 6px; transition: color 0.15s, background 0.15s; }
.breadcrumb:hover { color: var(--brand); background: var(--brand-light); }
.breadcrumb .material-icons-round { font-size: 14px; }
/* ===== SECTION HEADER ===== */
.section-header { margin-bottom: 24px; padding-bottom: 20px; border-bottom: 2px solid var(--border); }
.section-header__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.section-header__title { font-size: 22px; font-weight: 700; color: var(--text-primary); line-height: 1.3; letter-spacing: -0.02em; }
.section-header__meta { font-size: 12px; color: var(--text-tertiary); margin-top: 6px; display: flex; align-items: center; gap: 8px; }
.section-header__meta-dot { width: 3px; height: 3px; background: var(--border); border-radius: 50%; }
.section-header__actions { display: flex; gap: 6px; flex-shrink: 0; padding-top: 4px; }
.btn-ghost { padding: 6px 12px; border-radius: var(--radius); font-size: 12px; font-weight: 600; color: var(--text-secondary); border: 1px solid var(--border); background: var(--bg-surface); transition: all 0.15s; display: flex; align-items: center; gap: 5px; white-space: nowrap; }
.btn-ghost:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-light); }
.btn-ghost .material-icons-round { font-size: 15px; }
/* ===== SOI CARD ===== */
.soi-card { background: var(--brand-light); border-left: 4px solid var(--brand); border-radius: var(--radius); padding: 16px 20px; margin-bottom: 20px; }
.soi-card__label { font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--brand); margin-bottom: 8px; letter-spacing: 0.06em; }
.soi-card__text { font-size: 14px; color: var(--text-secondary); line-height: 1.8; }
/* ===== CLAUSE CARD ===== */
.clause-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 22px; margin-bottom: 12px; transition: border-color 0.15s, box-shadow 0.15s; box-shadow: var(--shadow); }
.clause-card:hover { border-color: var(--brand); box-shadow: 0 2px 8px rgba(0,133,115,0.12); }
.clause-card:focus { outline: none; }
.clause-card.highlighted { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(0,133,115,0.18), var(--shadow-md); }
.clause-card__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.clause-card__id { display: inline-flex; align-items: center; background: var(--brand); color: white; padding: 4px 10px; border-radius: 4px; font-size: 11.5px; font-weight: 600; font-family: 'JetBrains Mono', monospace; letter-spacing: 0.02em; }
.clause-card__actions { display: flex; align-items: center; gap: 4px; }
.clause-fav-btn { padding: 6px; border-radius: 6px; color: var(--text-tertiary); transition: all 0.15s; display: flex; align-items: center; }
.clause-fav-btn:hover { color: var(--fav); background: var(--accent-bg); }
.clause-fav-btn.active { color: var(--fav); }
.clause-fav-btn .material-icons-round { font-size: 18px; }
.clause-anchor-btn { padding: 6px; border-radius: 6px; color: var(--text-tertiary); transition: all 0.15s; display: flex; align-items: center; text-decoration: none; }
.clause-anchor-btn:hover { color: var(--brand); background: var(--bg-muted); }
.clause-anchor-btn .material-icons-round { font-size: 16px; }
.clause-card__text { font-size: 14px; font-weight: 500; line-height: 1.8; color: var(--text-primary); }
.clause-card__text mark { background: var(--highlight-bg); color: var(--highlight-text); border-radius: 2px; padding: 0 2px; font-style: normal; font-weight: inherit; }
/* ===== INTERPRETATION CARD ===== */
.interp-card { background: var(--accent-bg); border: 1px solid rgba(245,158,11,0.35); border-left: 4px solid var(--accent); border-radius: var(--radius); margin-top: 14px; overflow: hidden; }
.interp-card__header { display: flex; align-items: center; gap: 10px; padding: 12px 16px; background: var(--accent-bg-header); cursor: pointer; transition: background 0.15s; user-select: none; }
.interp-card__header:hover { background: var(--accent-bg-hover); }
.interp-card__header:focus-visible { outline: 2px solid var(--accent-dark); outline-offset: -2px; }
.interp-card__icon { color: var(--accent-dark); font-size: 17px; flex-shrink: 0; }
.interp-card__label { font-size: 10.5px; font-weight: 700; text-transform: uppercase; color: var(--accent-dark); flex: 1; letter-spacing: 0.06em; }
.interp-card__body { max-height: 0; overflow: hidden; transition: max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1); }
.interp-card.expanded .interp-card__body { max-height: 4000px; }
.interp-card__toggle { color: var(--accent-dark); font-size: 20px; transition: transform 0.3s ease; flex-shrink: 0; }
.interp-card.expanded .interp-card__toggle { transform: rotate(180deg); }
.interp-card__content { padding: 16px 18px; font-size: 14px; color: var(--text-primary); line-height: 1.9; overflow-wrap: break-word; }
.interp-card__content > * { min-height: 0; }
.interp-card__content p { margin-bottom: 12px; }
.interp-card__content p:last-child { margin-bottom: 0; }
.interp-card__content .bullet-item { display: block; margin: 6px 0 6px 16px; padding: 6px 10px 6px 12px; border-left: 3px solid var(--accent); color: var(--text-secondary); font-size: 13.5px; background: rgba(245,158,11,0.06); border-radius: 0 4px 4px 0; }
[data-theme="dark"] .interp-card { background: #1e1a17; border-color: #b45309; }
[data-theme="dark"] .interp-card__header { background: #1c1917; border-bottom: 1px solid #92400e; }
[data-theme="dark"] .interp-card__header:hover { background: #292524; }
[data-theme="dark"] .interp-card__icon,
[data-theme="dark"] .interp-card__label,
[data-theme="dark"] .interp-card__toggle { color: #fbbf24; }
[data-theme="dark"] .interp-card__content { color: #e2e8f0; }
[data-theme="dark"] .interp-card__content .bullet-item { color: #94a3b8; border-color: #d97706; background: rgba(217,119,6,0.08); }
[data-theme="dark"] .soi-card { background: var(--brand-light); }
[data-theme="dark"] .soi-card__text { color: var(--text-secondary); }
[data-theme="dark"] .clause-card { background: var(--bg-surface); border-color: var(--border); }
[data-theme="dark"] .clause-card:hover { border-color: var(--brand); }
/* ===== TOAST ===== */
.toast { position: fixed; bottom: 24px; right: 24px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 20px; box-shadow: var(--shadow-md); display: flex; align-items: center; gap: 10px; z-index: 1000; transform: translateY(100px); opacity: 0; transition: transform 0.3s cubic-bezier(0.4,0,0.2,1), opacity 0.3s ease; font-size: 13px; font-weight: 500; }
.toast.show { transform: translateY(0); opacity: 1; }
.toast .material-icons-round { font-size: 17px; color: var(--brand); }
/* ===== LOADER ===== */
.loader { position: fixed; inset: 0; background: var(--bg-surface); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 9999; gap: 14px; transition: opacity 0.3s, visibility 0.3s; }
.loader.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
.loader__spinner { width: 34px; height: 34px; border: 3px solid var(--bg-muted); border-top-color: var(--brand); border-radius: 50%; animation: spin 0.75s linear infinite; }
.loader span { font-size: 13px; color: var(--text-tertiary); }
@keyframes spin { to { transform: rotate(360deg); } }
/* ===== ACCESSIBILITY ===== */
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
/* ===== PRINT ===== */
@media print {
.sidebar, .header, .search-panel, .toast, .loader,
.clause-card__actions, .section-header__actions, .breadcrumb { display: none !important; }
html, body { overflow: visible; height: auto; background: white; color: black; }
.app-layout { display: block; }
.content-wrapper, .content { overflow: visible; height: auto; }
.interp-card__body { max-height: none !important; overflow: visible !important; }
.clause-card { break-inside: avoid; box-shadow: none; border: 1px solid #d1d5db; margin-bottom: 8px; }
.interp-card { break-inside: avoid; }
.content__inner { max-width: 100%; }
.section-header { page-break-after: avoid; }
.soi-card { background: #f5f5f5; border-left-color: #333; }
.clause-card__id { background: #333; color: white; }
.clause-card__text, .interp-card__content { color: #111; }
}
/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
.sidebar { position: fixed; left: -100%; z-index: 1000; height: 100%; transition: left 0.3s cubic-bezier(0.4,0,0.2,1); box-shadow: none; }
.sidebar.open { left: 0; box-shadow: var(--shadow-md); }
.sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 999; backdrop-filter: blur(2px); }
.sidebar-overlay.visible { display: block; }
.header { padding: 0 12px; gap: 8px; }
.header__search { max-width: 180px; }
.content { padding: 20px 16px; }
.home-grid { grid-template-columns: 1fr 1fr; gap: 8px; }
.section-header__title { font-size: 18px; }
.section-header__actions { display: none; }
.home-hero { padding: 22px 18px; }
.home-hero h2 { font-size: 17px; }
.home-hero__meta { gap: 6px; }
.section-header__top { flex-direction: column; }
}
@media (max-width: 420px) {
.home-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="app-layout">
<!-- Mobile overlay -->
<div class="sidebar-overlay" id="sidebar-overlay"></div>
<!-- Sidebar -->
<aside class="sidebar" id="sidebar" aria-label="Navigation">
<div class="sidebar__brand" onclick="renderHome()" tabindex="0" role="button" aria-label="Home">
<h1>BRCGS V9 Navigator</h1>
<p>Food Safety Issue 9</p>
</div>
<nav class="sidebar__nav" id="section-nav" aria-label="Sections"></nav>
<div class="sidebar__footer">
<div class="sidebar__read-label">Reading progress</div>
<div class="sidebar__read-bar"><div class="sidebar__read-fill" id="read-fill" style="width:0%"></div></div>
<div class="sidebar__read-text" id="read-text">0 / 9 sections</div>
</div>
</aside>
<!-- Main -->
<main class="main">
<header class="header" role="banner">
<button class="header__btn" id="menu-toggle" aria-label="Toggle menu" aria-expanded="false">
<span class="material-icons-round">menu</span>
</button>
<div class="header__search">
<span class="material-icons-round header__search-icon">search</span>
<input type="text" class="header__search-input" id="search-input"
placeholder="Search clauses…" aria-label="Search clauses" autocomplete="off" spellcheck="false">
<span class="material-icons-round header__search-clear" id="search-clear" role="button" aria-label="Clear search" tabindex="0">close</span>
</div>
<div class="header__spacer"></div>
<div class="header__actions">
<button class="header__btn" id="density-toggle" aria-label="Toggle view density" title="Compact/Normal view">
<span class="material-icons-round" id="density-icon">view_agenda</span>
</button>
<button class="header__btn" id="print-btn" aria-label="Print" title="Print current view">
<span class="material-icons-round">print</span>
</button>
<button class="header__btn" id="theme-toggle" aria-label="Toggle dark mode">
<span class="material-icons-round" id="theme-icon">dark_mode</span>
</button>
</div>
</header>
<!-- Search results -->
<div class="search-panel" id="search-panel" role="listbox" aria-label="Search results"></div>
<div class="content-wrapper">
<div class="content" id="content" role="main">
<div class="content__inner" id="view-port"></div>
</div>
</div>
</main>
</div>
<div class="toast" id="toast" role="status" aria-live="polite">
<span class="material-icons-round" id="toast-icon" style="font-size:16px">info</span>
<span id="toast-text">Done</span>
</div>
<div class="loader" id="loader" aria-label="Loading">
<div class="loader__spinner"></div>
<span style="font-size:13px;color:var(--text-tertiary)">Loading data…</span>
</div>
<script src="BRCGS_data.js"></script>
<script>
// ===== STATE =====
var BRCGS_DATA = [];
var currentSecIdx = null;
var currentTheme = localStorage.getItem('brcgs_v9_theme') || 'light';
var EXPANDED = JSON.parse(localStorage.getItem('brcgs_expanded') || '{}');
var FAVORITES = JSON.parse(localStorage.getItem('brcgs_favorites') || '[]');
var READ_SECTIONS = JSON.parse(localStorage.getItem('brcgs_read') || '[]');
var VIEW_DENSITY = localStorage.getItem('brcgs_density') || 'normal';
document.documentElement.setAttribute('data-theme', currentTheme);
document.getElementById('theme-icon').textContent = currentTheme === 'light' ? 'dark_mode' : 'light_mode';
document.body.classList.add('view-density-' + VIEW_DENSITY);
// ===== UTILITIES =====
function showToast(msg, icon) {
icon = icon || 'info';
document.getElementById('toast-text').textContent = msg;
document.getElementById('toast-icon').textContent = icon;
var t = document.getElementById('toast');
t.classList.add('show');
clearTimeout(t._timer);
t._timer = setTimeout(function() { t.classList.remove('show'); }, 2200);
}
function saveExpanded(id, val) {
EXPANDED[id] = val;
localStorage.setItem('brcgs_expanded', JSON.stringify(EXPANDED));
}
function escapeHtml(text) {
var d = document.createElement('div');
d.textContent = text;
return d.innerHTML;
}
function highlightQuery(text, q) {
if (!q) return escapeHtml(text);
var esc = escapeHtml(text);
var escQ = q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return esc.replace(new RegExp('(' + escQ + ')', 'gi'), '<mark>$1</mark>');
}
function splitIntoParagraphs(text) {
if (!text) return '';
text = text.replace(/\n/g, ' ').replace(/ +/g, ' ').trim();
var sentences = text.split(/(?<=[.!?])\s+(?=[A-Z])/);
return sentences.map(function(s) {
s = s.trim();
if (!s) return '';
if (s.match(/^[•\-*]\s/) || s.match(/^\d+[.)]\s/))
return '<span class="bullet-item">' + escapeHtml(s) + '</span>';
return '<p>' + escapeHtml(s) + '</p>';
}).join('');
}
function isFav(id) { return FAVORITES.indexOf(id) !== -1; }
function toggleFav(id) {
var idx = FAVORITES.indexOf(id);
if (idx === -1) { FAVORITES.push(id); showToast('Added to favourites', 'star'); }
else { FAVORITES.splice(idx, 1); showToast('Removed from favourites', 'star_border'); }
localStorage.setItem('brcgs_favorites', JSON.stringify(FAVORITES));
document.querySelectorAll('.clause-fav-btn[data-id="' + id + '"]').forEach(function(btn) {
var f = isFav(id);
btn.classList.toggle('active', f);
btn.querySelector('.material-icons-round').textContent = f ? 'star' : 'star_border';
btn.setAttribute('aria-pressed', f);
btn.title = f ? 'Remove from favourites' : 'Add to favourites';
});
}
function toggleInterp(id) {
var card = document.getElementById('interp-' + id);
if (!card) return;
var expanded = card.classList.toggle('expanded');
card.querySelector('.interp-card__header').setAttribute('aria-expanded', expanded);
saveExpanded(id, expanded);
}
// ===== BUILD CLAUSE CARD =====
function buildClauseCard(cl, searchQ) {
var f = isFav(cl.id);
var exp = EXPANDED[cl.id] || false;
var reqHtml = searchQ ? highlightQuery(cl.requirement, searchQ) : escapeHtml(cl.requirement);
var interpHtml = '';
if (cl.interpretation) {
interpHtml = '<div class="interp-card' + (exp ? ' expanded' : '') + '" id="interp-' + cl.id + '">' +
'<div class="interp-card__header" onclick="toggleInterp(\'' + cl.id + '\')" ' +
'role="button" tabindex="0" aria-expanded="' + exp + '" ' +
'onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();toggleInterp(\'' + cl.id + '\')}">' +
'<span class="material-icons-round interp-card__icon">menu_book</span>' +
'<span class="interp-card__label">Expert Interpretation</span>' +
'<span class="material-icons-round interp-card__toggle">expand_more</span>' +
'</div>' +
'<div class="interp-card__body"><div class="interp-card__content">' +
splitIntoParagraphs(cl.interpretation) +
'</div></div></div>';
}
return '<article class="clause-card" id="clause-' + cl.id + '" tabindex="-1">' +
'<div class="clause-card__header">' +
'<span class="clause-card__id">' + cl.id + '</span>' +
'<div class="clause-card__actions">' +
'<button class="clause-fav-btn' + (f ? ' active' : '') + '" data-id="' + cl.id + '" ' +
'onclick="toggleFav(\'' + cl.id + '\')" aria-pressed="' + f + '" ' +
'title="' + (f ? 'Remove from favourites' : 'Add to favourites') + '">' +
'<span class="material-icons-round">' + (f ? 'star' : 'star_border') + '</span></button>' +
'<a class="clause-anchor-btn" href="#clause-' + cl.id + '" ' +
'title="Copy link to clause" aria-label="Link to clause ' + cl.id + '" ' +
'onclick="copyClauseLink(\'' + cl.id + '\');return false;">' +
'<span class="material-icons-round">link</span></a>' +
'</div>' +
'</div>' +
'<p class="clause-card__text">' + reqHtml + '</p>' +
interpHtml +
'</article>';
}
function copyClauseLink(id) {
var url = window.location.href.split('#')[0] + '#clause-' + id;
if (navigator.clipboard) {
navigator.clipboard.writeText(url).then(function() { showToast('Link copied', 'link'); });
} else {
showToast('Clause: ' + id, 'link');
}
}
// ===== RENDER HOME =====
function renderHome() {
currentSecIdx = null;
updateNavActive(-1);
var totalClauses = BRCGS_DATA.reduce(function(a, s) { return a + s.clauses.length; }, 0);
var totalInterp = BRCGS_DATA.reduce(function(a, s) {
return a + s.clauses.filter(function(c) { return c.interpretation; }).length;
}, 0);
var cardsHtml = BRCGS_DATA.map(function(sec, idx) {
return '<div class="home-card" onclick="renderSection(' + idx + ')" tabindex="0" role="button" ' +
'aria-label="Section ' + sec.section + ': ' + escapeHtml(sec.title) + '">' +
'<div class="home-card__num">Section ' + sec.section + '</div>' +
'<div class="home-card__title">' + escapeHtml(sec.title) + '</div>' +
'<div class="home-card__footer">' +
'<span class="home-card__count">' + sec.clauses.length + ' clauses</span>' +
'<span class="material-icons-round home-card__arrow">arrow_forward</span>' +
'</div></div>';
}).join('');
document.getElementById('view-port').innerHTML =
'<div class="home-hero">' +
'<h2>BRCGS Food Safety Issue 9</h2>' +
'<p>Expert interpretation guide for all 9 sections. Read official clause requirements with detailed guidance for auditors and food safety professionals.</p>' +
'<div class="home-hero__meta">' +
'<div class="home-hero__badge"><span class="material-icons-round">layers</span>' + BRCGS_DATA.length + ' sections</div>' +
'<div class="home-hero__badge"><span class="material-icons-round">article</span>' + totalClauses + ' clauses</div>' +
'<div class="home-hero__badge"><span class="material-icons-round">menu_book</span>' + totalInterp + ' interpretations</div>' +
'</div>' +
'</div>' +
'<p class="home-sections-title">Browse by section</p>' +
'<div class="home-grid">' + cardsHtml + '</div>';
document.getElementById('content').scrollTop = 0;
}
// ===== RENDER SECTION =====
function renderSection(secIdx, searchQ, scrollToId) {
currentSecIdx = secIdx;
var sec = BRCGS_DATA[secIdx];
updateNavActive(secIdx);
markSectionRead(secIdx);
updateSidebarReadStatus();
var interpCount = sec.clauses.filter(function(c) { return c.interpretation; }).length;
var soiHtml = sec.statement_of_intent ?
'<div class="soi-card"><div class="soi-card__label">Statement of Intent</div>' +
'<div class="soi-card__text">' + escapeHtml(sec.statement_of_intent) + '</div></div>' : '';
var html =
'<section class="section-header">' +
'<div class="breadcrumb" onclick="renderHome()" role="button" tabindex="0" ' +
'onkeydown="if(event.key===\'Enter\'){renderHome()}" aria-label="Back to all sections">' +
'<span class="material-icons-round">arrow_back</span>' +
'<span>All Sections</span>' +
'</div>' +
'<div class="section-header__top">' +
'<div>' +
'<h2 class="section-header__title">Section ' + sec.section + ' — ' + escapeHtml(sec.title) + '</h2>' +
'<div class="section-header__meta">' +
'<span>' + sec.clauses.length + ' clauses</span>' +
'<span class="section-header__meta-dot"></span>' +
'<span>' + interpCount + ' interpretations</span>' +
'</div>' +
'</div>' +
'<div class="section-header__actions">' +
'<button class="btn-ghost" onclick="setAllInterp(' + secIdx + ',true)" title="Expand all">' +
'<span class="material-icons-round">unfold_more</span>Expand all' +
'</button>' +
'<button class="btn-ghost" onclick="setAllInterp(' + secIdx + ',false)" title="Collapse all">' +
'<span class="material-icons-round">unfold_less</span>Collapse' +
'</button>' +
'</div>' +
'</div>' +
'</section>' + soiHtml;
sec.clauses.forEach(function(cl) { html += buildClauseCard(cl, searchQ || null); });
document.getElementById('view-port').innerHTML = html;
document.getElementById('content').scrollTop = 0;
if (scrollToId) {
setTimeout(function() {
var el = document.getElementById('clause-' + scrollToId);
if (!el) return;
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
el.classList.add('highlighted');
var interp = document.getElementById('interp-' + scrollToId);
if (interp && !interp.classList.contains('expanded')) {
interp.classList.add('expanded');
var hdr = interp.querySelector('.interp-card__header');
if (hdr) hdr.setAttribute('aria-expanded', 'true');
}
}, 80);
}
}
function setAllInterp(secIdx, expand) {
BRCGS_DATA[secIdx].clauses.forEach(function(cl) {
if (!cl.interpretation) return;
var card = document.getElementById('interp-' + cl.id);
if (!card) return;
card.classList.toggle('expanded', expand);
var hdr = card.querySelector('.interp-card__header');
if (hdr) hdr.setAttribute('aria-expanded', expand);
saveExpanded(cl.id, expand);
});
}
// ===== SIDEBAR =====
function renderSidebar() {
var nav = document.getElementById('section-nav');
nav.innerHTML = '<div class="nav-group__header">Navigate</div>' +
'<div class="nav-item" id="nav-home" onclick="renderHome();closeMobileSidebar()" tabindex="0" role="button">' +
'<span class="material-icons-round">home</span>' +
'<span class="nav-item__label">Home</span></div>';
BRCGS_DATA.forEach(function(sec, idx) {
var el = document.createElement('div');
el.className = 'nav-item';
el.id = 'nav-sec-' + idx;
el.tabIndex = 0;
el.setAttribute('role', 'button');
el.setAttribute('aria-label', 'Section ' + sec.section + ': ' + sec.title);
el.innerHTML =
'<span class="material-icons-round">folder_open</span>' +
'<span class="nav-item__label">' + sec.section + '. ' + sec.title.substring(0, 21) + (sec.title.length > 21 ? '…' : '') + '</span>' +
'<span class="nav-badge">' + sec.clauses.length + '</span>';
el.onclick = function() { renderSection(idx); closeMobileSidebar(); };
el.onkeydown = function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); renderSection(idx); closeMobileSidebar(); } };
nav.appendChild(el);
});
}
function updateNavActive(secIdx) {
document.querySelectorAll('.nav-item').forEach(function(el) { el.classList.remove('active'); });
var target = secIdx === -1 ? document.getElementById('nav-home') : document.getElementById('nav-sec-' + secIdx);
if (target) { target.classList.add('active'); target.scrollIntoView({ block: 'nearest' }); }
}
// ===== SEARCH =====
var _searchTimer;
function closeSearch() {
var panel = document.getElementById('search-panel');
panel.classList.remove('open');
panel.innerHTML = '';
}
document.getElementById('search-input').addEventListener('input', function() {
clearTimeout(_searchTimer);
var q = this.value.trim();
document.getElementById('search-clear').classList.toggle('visible', q.length > 0);
if (q.length < 2) { closeSearch(); return; }
_searchTimer = setTimeout(function() { doSearch(q); }, 200);
});
document.getElementById('search-input').addEventListener('keydown', function(e) {
if (e.key === 'Escape') { clearSearch(); }
if (e.key === 'ArrowDown') {
e.preventDefault();
var first = document.querySelector('.search-result');
if (first) first.focus();
}
});
document.getElementById('search-clear').addEventListener('click', clearSearch);
function clearSearch() {
document.getElementById('search-input').value = '';
document.getElementById('search-clear').classList.remove('visible');
closeSearch();
document.getElementById('search-input').focus();
}
function doSearch(q) {
var results = [];
var ql = q.toLowerCase();
BRCGS_DATA.forEach(function(sec, si) {
sec.clauses.forEach(function(cl) {
if (cl.id.toLowerCase().includes(ql) ||
cl.requirement.toLowerCase().includes(ql) ||
(cl.interpretation && cl.interpretation.toLowerCase().includes(ql))) {
results.push({ si: si, cl: cl, sec: sec });
}
});
});
var panel = document.getElementById('search-panel');
if (!results.length) {
panel.innerHTML = '<div class="search-empty">No results for "' + escapeHtml(q) + '"</div>';
} else {
var shown = results.slice(0, 18);
panel.innerHTML = '<div class="search-panel__header">' + results.length + ' result' + (results.length > 1 ? 's' : '') + '</div>' +
shown.map(function(r) {
var snippet = r.cl.requirement.length > 95 ? r.cl.requirement.slice(0, 95) + '…' : r.cl.requirement;
return '<div class="search-result" tabindex="0" role="option" ' +
'onclick="selectResult(' + r.si + ',\'' + r.cl.id + '\',\'' + escapeHtml(q).replace(/'/g, "\\'") + '\')" ' +
'onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();selectResult(' + r.si + ',\'' + r.cl.id + '\',\'' + escapeHtml(q).replace(/'/g, "\\'") + '\')}">' +
'<span class="search-result__id">' + r.cl.id + '</span>' +
'<div class="search-result__body">' +
'<div class="search-result__section">Section ' + r.sec.section + ' — ' + escapeHtml(r.sec.title.substring(0, 30)) + '</div>' +
'<div class="search-result__text">' + highlightQuery(snippet, q) + '</div>' +
'</div></div>';
}).join('') +
(results.length > 18 ? '<div class="search-more">+ ' + (results.length - 18) + ' more — refine your search</div>' : '');
}
panel.classList.add('open');
}
function selectResult(secIdx, clauseId, q) {
clearSearch();
renderSection(secIdx, q, clauseId);
}
document.addEventListener('click', function(e) {
var panel = document.getElementById('search-panel');
var inp = document.getElementById('search-input');
if (!panel.contains(e.target) && e.target !== inp) closeSearch();
});
// ===== KEYBOARD NAV (arrows between clauses) =====
document.getElementById('content').addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
var cards = Array.from(document.querySelectorAll('.clause-card'));
if (!cards.length) return;
var idx = cards.indexOf(document.activeElement);
if (e.key === 'ArrowDown') {
e.preventDefault();
var next = idx < cards.length - 1 ? cards[idx + 1] : (idx === -1 ? cards[0] : cards[idx]);
next.focus();
next.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} else if (e.key === 'ArrowUp') {
e.preventDefault();
var prev = idx > 0 ? cards[idx - 1] : cards[0];
prev.focus();
prev.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
});
// ===== MOBILE SIDEBAR =====
function closeMobileSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebar-overlay').classList.remove('visible');
document.getElementById('menu-toggle').setAttribute('aria-expanded', 'false');
}
document.getElementById('menu-toggle').onclick = function() {
var open = document.getElementById('sidebar').classList.toggle('open');
document.getElementById('sidebar-overlay').classList.toggle('visible', open);
this.setAttribute('aria-expanded', open);
};
document.getElementById('sidebar-overlay').onclick = closeMobileSidebar;
// ===== PRINT =====
document.getElementById('print-btn').onclick = function() {
if (currentSecIdx !== null) setAllInterp(currentSecIdx, true);
setTimeout(function() { window.print(); }, 150);
};
// ===== VIEW DENSITY =====
document.getElementById('density-toggle').onclick = function() {
VIEW_DENSITY = VIEW_DENSITY === 'normal' ? 'compact' : 'normal';
localStorage.setItem('brcgs_density', VIEW_DENSITY);
document.body.classList.remove('view-density-normal', 'view-density-compact');
document.body.classList.add('view-density-' + VIEW_DENSITY);
document.getElementById('density-icon').textContent = VIEW_DENSITY === 'compact' ? 'view_list' : 'view_agenda';
showToast(VIEW_DENSITY === 'compact' ? 'Compact view' : 'Normal view');
};
// ===== MARK SECTION READ =====
function markSectionRead(secIdx) {
var secId = BRCGS_DATA[secIdx].section.toString();
if (READ_SECTIONS.indexOf(secId) === -1) {
READ_SECTIONS.push(secId);
localStorage.setItem('brcgs_read', JSON.stringify(READ_SECTIONS));
}
updateReadProgressBar();
}
function updateReadProgressBar() {
var fill = document.getElementById('read-fill');
var text = document.getElementById('read-text');
if (!fill || !text) return;
var pct = Math.round((READ_SECTIONS.length / BRCGS_DATA.length) * 100);
fill.style.width = pct + '%';
text.textContent = READ_SECTIONS.length + ' / ' + BRCGS_DATA.length + ' sections';
}
function isSectionRead(secId) {
return READ_SECTIONS.indexOf(secId.toString()) !== -1;
}
function updateSidebarReadStatus() {
BRCGS_DATA.forEach(function(sec, idx) {
var navItem = document.getElementById('nav-sec-' + idx);
if (navItem) {
navItem.classList.toggle('nav-item--visited', isSectionRead(sec.section));
}
});
}
// ===== THEME =====
document.getElementById('theme-toggle').onclick = function() {
currentTheme = currentTheme === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', currentTheme);
localStorage.setItem('brcgs_v9_theme', currentTheme);
document.getElementById('theme-icon').textContent = currentTheme === 'light' ? 'dark_mode' : 'light_mode';
};
// ===== INIT =====
setTimeout(function() {
if (typeof BRCGS_DATA_EXT !== 'undefined') BRCGS_DATA = BRCGS_DATA_EXT;
document.getElementById('loader').classList.add('hidden');
renderSidebar();
updateSidebarReadStatus();
updateReadProgressBar();
// Handle direct anchor link to a clause
var hash = window.location.hash;
if (hash && hash.startsWith('#clause-')) {
var clauseId = hash.slice(8);
var found = false;
BRCGS_DATA.forEach(function(sec, si) {
if (found) return;
sec.clauses.forEach(function(cl) {
if (found) return;
if (cl.id === clauseId) { renderSection(si, null, clauseId); found = true; }
});
});
if (!found) renderHome();
} else {
renderHome();
}
}, 100);
</script>
</body>
</html>