| <!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; } |
| |
| |
| [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; 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-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; } |
| |
| |
| .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-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-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; 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-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 { 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); 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 { 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 { 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 { 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; } |
| |
| |
| .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 { 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 { 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); } } |
| |
| |
| :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; } |
| |
| |
| @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; } |
| } |
| |
| |
| @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"> |
| |
| <div class="sidebar-overlay" id="sidebar-overlay"></div> |
|
|
| |
| <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 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> |
|
|
| |
| <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> |
| |
| 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); |
| |
| |
| 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); |
| } |
| |
| |
| 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'); |
| } |
| } |
| |
| |
| 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; |
| } |
| |
| |
| 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); |
| }); |
| } |
| |
| |
| 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' }); } |
| } |
| |
| |
| 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(); |
| }); |
| |
| |
| 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' }); |
| } |
| }); |
| |
| |
| 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; |
| |
| |
| document.getElementById('print-btn').onclick = function() { |
| if (currentSecIdx !== null) setAllInterp(currentSecIdx, true); |
| setTimeout(function() { window.print(); }, 150); |
| }; |
| |
| |
| 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'); |
| }; |
| |
| |
| 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)); |
| } |
| }); |
| } |
| |
| |
| 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'; |
| }; |
| |
| |
| setTimeout(function() { |
| if (typeof BRCGS_DATA_EXT !== 'undefined') BRCGS_DATA = BRCGS_DATA_EXT; |
| document.getElementById('loader').classList.add('hidden'); |
| renderSidebar(); |
| updateSidebarReadStatus(); |
| updateReadProgressBar(); |
| |
| |
| 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> |
|
|