diff --git a/css/styles.css b/css/styles.css index 002c2af..5d67915 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1,744 +1,770 @@ -/* ===== Design Tokens ===== */ -:root { - --bg-primary: #0d1117; - --bg-secondary: #161b22; - --bg-tertiary: #21262d; - - --text-primary: #f0f6fc; - --text-secondary: #b1bac4; - --text-tertiary: #7d8590; - - --border-color: #30363d; - - --brand-primary: #2b0a3d; - --brand-dark: #180422; - --brand-accent: #8b5cf6; - - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35); - --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.5); - - --radius-sm: 10px; - --radius-md: 16px; - --radius-lg: 22px; - - --transition: 0.2s ease; -} - -/* ===== Reset ===== */ -*, -*::before, -*::after { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -/* ===== Base ===== */ -html { - scroll-behavior: smooth; -} - -body { - font-family: - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Roboto, - Helvetica, - Arial, - sans-serif; - background: var(--bg-primary); - color: var(--text-primary); - min-height: 100vh; - line-height: 1.6; - /* Guard against any child overflowing horizontally and pulling the page - wider than the viewport, which leaves a blank strip beside the header. */ - overflow-x: hidden; -} - -/* ===== Header ===== */ -header { - background: - radial-gradient(circle at top left, rgba(139, 92, 246, 0.15), transparent 45%), - linear-gradient(135deg, var(--brand-primary), var(--brand-dark)); - color: white; - padding: 1.5rem 2rem 1rem; - box-shadow: var(--shadow-lg); -} - -.header-content { - max-width: 1440px; - margin: 0 auto; -} - -.logo { - display: grid; - grid-template-columns: auto 1fr auto; - align-items: center; - gap: 1rem; - width: 100%; -} - -.site-logo { - width: 110px; - height: 110px; - object-fit: contain; - flex-shrink: 0; -} - -[data-theme="dark"] .site-logo { - content: url("../security-libre-devops-white.png"); -} - -[data-theme="light"] .site-logo { - content: url("../security-libre-devops-white.png"); -} - -.logo-text { - text-align: center; -} - -.logo h1 { - font-size: 2rem; - font-weight: 800; - line-height: 1.15; -} - -.logo .subtitle { - margin-top: 0.35rem; - font-size: 1rem; - color: rgba(255, 255, 255, 0.88); -} - -.logo-spacer { - width: 110px; -} - -/* ===== Header Actions ===== */ -.header-actions { - display: flex; - justify-content: center; - gap: 0.85rem; - flex-wrap: wrap; - margin-top: 1.25rem; -} - -/* ===== Search ===== */ -.search-box { - width: 100%; - max-width: 760px; - position: relative; -} - -.search-box .search-icon { - position: absolute; - left: 1rem; - top: 50%; - transform: translateY(-50%); - opacity: 0.8; -} - -.search-box input { - width: 100%; - padding: 0.95rem 1rem 0.95rem 3rem; - border: none; - border-radius: 999px; - background: rgba(255, 255, 255, 0.14); - color: white; - font-size: 1rem; - outline: none; - backdrop-filter: blur(12px); -} - -.search-box input::placeholder { - color: rgba(255, 255, 255, 0.72); -} - -/* ===== Buttons ===== */ -#bookmarks-toggle, -#theme-toggle, -.rss-button { - border: 1px solid rgba(255, 255, 255, 0.18); - background: rgba(255, 255, 255, 0.08); - color: white; - cursor: pointer; - transition: all var(--transition); - backdrop-filter: blur(10px); -} - -#bookmarks-toggle, -.rss-button { - padding: 0.75rem 1.2rem; - border-radius: 999px; - font-size: 0.95rem; - text-decoration: none; -} - -#theme-toggle { - width: 52px; - height: 52px; - border-radius: 50%; - font-size: 1.4rem; -} - -#bookmarks-toggle:hover, -#theme-toggle:hover, -.rss-button:hover { - background: rgba(255, 255, 255, 0.16); -} - -#bookmarks-toggle.active { - background: rgba(139, 92, 246, 0.35); - border-color: var(--brand-accent); -} - -/* ===== Stats ===== */ -.stats-bar { - max-width: 1440px; - margin: 1rem auto 0; - display: flex; - justify-content: center; - gap: 1.25rem; - flex-wrap: wrap; - font-size: 0.95rem; - color: rgba(255, 255, 255, 0.82); -} - -/* ===== Filters ===== */ -.filters { - background: var(--bg-secondary); - border-bottom: 1px solid var(--border-color); - padding: 0.8rem 1.5rem; -} - -.filter-section-title { - max-width: 1440px; - margin: 0 auto 0.5rem; - font-size: 0.82rem; - font-weight: 700; - text-transform: uppercase; - color: var(--text-secondary); -} - -.filter-scroll { - max-width: 1440px; - margin: 0 auto; - overflow-x: auto; - overflow-y: hidden; - -webkit-overflow-scrolling: touch; - scrollbar-width: thin; -} - -.filter-pills { - display: flex; - gap: 0.5rem; - flex-wrap: nowrap; - padding-bottom: 0.25rem; -} - -.pill { - border: 1px solid var(--border-color); - background: var(--bg-primary); - color: var(--text-secondary); - padding: 0.5rem 0.9rem; - border-radius: 999px; - cursor: pointer; - white-space: nowrap; - font-size: 0.8rem; - flex-shrink: 0; - transition: all var(--transition); -} - -.pill:hover { - border-color: var(--brand-accent); -} - -.pill.active { - background: var(--brand-primary); - border-color: var(--brand-primary); - color: white; -} - -.pill .count { - margin-left: 0.4rem; - opacity: 0.85; -} - -/* ===== Main ===== */ -main { - max-width: 1440px; - margin: 0 auto; - padding: 1.5rem; -} - -/* ===== Controls ===== */ -.controls { - display: flex; - justify-content: space-between; - align-items: center; - gap: 1rem; - flex-wrap: wrap; - margin-bottom: 1.25rem; -} - -.controls-right { - display: flex; - gap: 0.75rem; -} - -#date-filter, -#sort-by { - padding: 0.75rem 1rem; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - color: var(--text-primary); - border-radius: 12px; - min-width: 160px; -} - -/* ===== Loading ===== */ -.loading { - display: none; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 1rem; - padding: 4rem 2rem; - color: var(--text-secondary); - font-size: 1rem; -} - -.loading.visible { - display: flex; -} - -.loading-spinner { - width: 40px; - height: 40px; - border: 3px solid var(--border-color); - border-top-color: var(--brand-accent); - border-radius: 50%; - animation: spin 0.8s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -/* ===== No Results ===== */ -.no-results { - display: none; - text-align: center; - padding: 4rem 2rem; - color: var(--text-secondary); -} - -.no-results.visible { - display: block; -} - -/* ===== Grid ===== */ -.articles-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); - gap: 1rem; -} - -/* ===== Cards ===== */ -.article-card { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - padding: 1.2rem; - transition: all var(--transition); - /* Column flexbox so the Read More button can be pushed to the bottom of the - card with margin-top:auto, instead of floating wherever the summary happens - to end. */ - display: flex; - flex-direction: column; - /* Allow the card to shrink below its content's intrinsic width inside the - grid track; without this a long unbreakable token (URL, hash) widens the - track and overflows the page on Chrome mobile. */ - min-width: 0; -} - -.article-card:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-md); - border-color: rgba(139, 92, 246, 0.3); -} - -.card-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 0.5rem; -} - -.blog-tag { - padding: 0.3rem 0.75rem; - border-radius: 999px; - font-size: 0.72rem; - font-weight: 700; -} - -.bookmark-btn { - background: none; - border: none; - font-size: 1.2rem; - cursor: pointer; - color: var(--text-secondary); - flex-shrink: 0; - align-self: flex-start; -} - -.bookmark-btn.bookmarked { - color: #f59e0b; -} - -.article-title { - font-size: 1.05rem; - font-weight: 700; - margin: 0.8rem 0 0.5rem; - overflow-wrap: anywhere; -} - -.article-title a { - color: var(--text-primary); - text-decoration: none; - transition: color var(--transition); -} - -.article-title a:hover { - color: var(--brand-accent); -} - -.article-meta { - display: flex; - gap: 0.5rem 1rem; - flex-wrap: wrap; - font-size: 0.8rem; - color: var(--text-tertiary); - margin-bottom: 0.75rem; - line-height: 1.4; - overflow-wrap: anywhere; -} - -.article-summary { - color: var(--text-secondary); - font-size: 0.92rem; - line-height: 1.55; - overflow-wrap: anywhere; - /* Explicit, so a full card (where margin-top:auto resolves to zero) still - keeps the button clear of the text. */ - margin-bottom: 1rem; -} - -/* ===== Toast ===== */ -.toast { - position: fixed; - bottom: 2rem; - left: 50%; - transform: translateX(-50%) translateY(1rem); - background: var(--bg-tertiary); - border: 1px solid var(--border-color); - color: var(--text-primary); - padding: 0.75rem 1.5rem; - border-radius: 999px; - opacity: 0; - pointer-events: none; - transition: all 0.25s ease; - z-index: 9999; -} - -.toast.visible { - opacity: 1; - transform: translateX(-50%) translateY(0); -} - -/* ===== Mobile ===== */ -@media (max-width: 768px) { - header { - padding: 1rem 1rem 1.25rem; - } - - .logo { - grid-template-columns: 1fr; - justify-items: center; - text-align: center; - gap: 0.75rem; - } - - .logo-spacer { - display: none; - } - - .site-logo { - width: 60px; - height: 60px; - } - - .logo h1 { - font-size: 2rem; - text-align: center; - } - - .logo .subtitle { - font-size: 0.95rem; - max-width: 320px; - } - - .header-actions { - margin-top: 1rem; - flex-direction: column; - align-items: center; - } - - .search-box { - width: 100%; - } - - .controls { - flex-direction: column; - align-items: stretch; - } - - .controls-right { - width: 100%; - } - - .controls-right select { - flex: 1; - min-width: unset; - } - - .articles-grid { - grid-template-columns: 1fr; - } - - .stats-bar { - gap: 0.75rem; - font-size: 0.85rem; - } -} - -@media (max-width: 480px) { - .logo h1 { - font-size: 1.75rem; - } - - .logo .subtitle { - font-size: 0.88rem; - } - - .search-box input { - font-size: 0.95rem; - padding: 0.85rem 1rem 0.85rem 2.75rem; - } - - .filter-pills { - gap: 0.4rem; - } - - .pill { - padding: 0.5rem 0.85rem; - font-size: 0.8rem; - } - - #bookmarks-toggle, - .rss-button { - width: 100%; - max-width: 420px; - display: flex; - align-items: center; - justify-content: center; - text-align: center; - } - - #theme-toggle { - align-self: center; - } - - .article-meta { - flex-direction: column; - gap: 0.35rem; - } -} - -/* ===== Light Theme ===== */ -[data-theme="light"] { - --bg-primary: #f6f8fa; - --bg-secondary: #ffffff; - --bg-tertiary: #eaeef2; - - --text-primary: #1c2128; - --text-secondary: #424a53; - --text-tertiary: #656d76; - - --border-color: #d0d7de; -} - -[data-theme="light"] .pill { - background: var(--bg-secondary); - color: var(--text-secondary); -} - -[data-theme="light"] .pill.active { - background: var(--brand-primary); - border-color: var(--brand-primary); - color: white; -} -/* --------------------------------------------------------------------------- - Full post viewer - - Only articles carrying a complete body (currently Microsoft Message Center - posts) get an expand button, because a Message Center permalink only opens - for someone with tenant admin access. Showing the post inline is the point. - Uses the native , so focus trapping and Escape come from the - platform. Both themes are driven by the existing custom properties. ---------------------------------------------------------------------------- */ - -.expand-btn { - /* Pinned to the bottom left of the card. Grid tracks already stretch cards in - a row to equal height, so with the card as a column flexbox `margin-top: - auto` eats the leftover space and every button lands on the same baseline - regardless of how long its summary runs. align-self stops it stretching to - the full card width. */ - margin-top: auto; - align-self: flex-start; - padding: 0.5rem 1rem; - font-size: 0.85rem; - font-weight: 600; - font-family: inherit; - color: var(--text-primary); - background: var(--bg-tertiary); - border: 1px solid var(--border-color); - border-radius: var(--radius-sm); - cursor: pointer; - transition: var(--transition); -} - -.expand-btn:hover { - border-color: var(--brand-accent); - color: var(--brand-accent); -} - -.expand-btn:focus-visible { - outline: 2px solid var(--brand-accent); - outline-offset: 2px; -} - -.article-dialog { - padding: 0; - border: none; - background: transparent; - max-width: min(760px, 92vw); - max-height: 88vh; - overflow: visible; -} - -.article-dialog::backdrop { - background: rgba(0, 0, 0, 0.65); - backdrop-filter: blur(2px); -} - -.dialog-panel { - position: relative; - max-height: 88vh; - overflow-y: auto; - padding: 2rem; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - box-shadow: var(--shadow-lg); -} - -.dialog-close { - position: sticky; - top: 0; - float: right; - width: 2rem; - height: 2rem; - font-size: 1.4rem; - line-height: 1; - font-family: inherit; - color: var(--text-secondary); - background: var(--bg-tertiary); - border: 1px solid var(--border-color); - border-radius: 50%; - cursor: pointer; - transition: var(--transition); -} - -.dialog-close:hover { - color: var(--text-primary); - border-color: var(--brand-accent); -} - -.dialog-tags { - display: flex; - gap: 0.5rem; - flex-wrap: wrap; - margin-bottom: 1rem; -} - -.dialog-title { - margin: 0 0 0.75rem; - font-size: 1.4rem; - line-height: 1.35; - color: var(--text-primary); -} - -.dialog-text { - margin-top: 1.25rem; - color: var(--text-secondary); - line-height: 1.7; -} - -.dialog-text p { - margin: 0 0 1rem; -} - -.dialog-subheading { - margin: 1.5rem 0 0.75rem; - font-size: 1rem; - color: var(--text-primary); -} - -.dialog-links { - margin: 0; - padding-left: 1.2rem; - color: var(--text-secondary); -} - -.dialog-links li { - margin-bottom: 0.4rem; -} - -.dialog-links a, -.dialog-source-link { - color: var(--brand-accent); - overflow-wrap: anywhere; -} - -.dialog-source-link { - display: inline-block; - margin-top: 1.5rem; - padding-top: 1.25rem; - border-top: 1px solid var(--border-color); - font-size: 0.9rem; - font-weight: 600; -} - -@media (max-width: 600px) { - .dialog-panel { - padding: 1.25rem; - } - - .dialog-title { - font-size: 1.2rem; - } -} +/* ===== Design Tokens ===== */ +:root { + --bg-primary: #0d1117; + --bg-secondary: #161b22; + --bg-tertiary: #21262d; + + --text-primary: #f0f6fc; + --text-secondary: #b1bac4; + --text-tertiary: #7d8590; + + --border-color: #30363d; + + --brand-primary: #2b0a3d; + --brand-dark: #180422; + --brand-accent: #8b5cf6; + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35); + --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.5); + + --radius-sm: 10px; + --radius-md: 16px; + --radius-lg: 22px; + + --transition: 0.2s ease; +} + +/* ===== Reset ===== */ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +/* ===== Base ===== */ +html { + scroll-behavior: smooth; +} + +body { + font-family: + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + Helvetica, + Arial, + sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + min-height: 100vh; + line-height: 1.6; + /* Guard against any child overflowing horizontally and pulling the page + wider than the viewport, which leaves a blank strip beside the header. */ + overflow-x: hidden; +} + +/* ===== Header ===== */ +header { + background: + radial-gradient(circle at top left, rgba(139, 92, 246, 0.15), transparent 45%), + linear-gradient(135deg, var(--brand-primary), var(--brand-dark)); + color: white; + padding: 1.5rem 2rem 1rem; + box-shadow: var(--shadow-lg); +} + +.header-content { + max-width: 1440px; + margin: 0 auto; +} + +.logo { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 1rem; + width: 100%; +} + +.site-logo { + width: 110px; + height: 110px; + object-fit: contain; + flex-shrink: 0; +} + +[data-theme="dark"] .site-logo { + content: url("../security-libre-devops-white.png"); +} + +[data-theme="light"] .site-logo { + content: url("../security-libre-devops-white.png"); +} + +.logo-text { + text-align: center; +} + +.logo h1 { + font-size: 2rem; + font-weight: 800; + line-height: 1.15; +} + +.logo .subtitle { + margin-top: 0.35rem; + font-size: 1rem; + color: rgba(255, 255, 255, 0.88); +} + +.logo-spacer { + width: 110px; +} + +/* ===== Header Actions ===== */ +.header-actions { + display: flex; + justify-content: center; + gap: 0.85rem; + flex-wrap: wrap; + margin-top: 1.25rem; +} + +/* ===== Search ===== */ +.search-box { + width: 100%; + max-width: 760px; + position: relative; +} + +.search-box .search-icon { + position: absolute; + left: 1rem; + top: 50%; + transform: translateY(-50%); + opacity: 0.8; +} + +.search-box input { + width: 100%; + padding: 0.95rem 1rem 0.95rem 3rem; + border: none; + border-radius: 999px; + background: rgba(255, 255, 255, 0.14); + color: white; + font-size: 1rem; + outline: none; + backdrop-filter: blur(12px); +} + +.search-box input::placeholder { + color: rgba(255, 255, 255, 0.72); +} + +/* ===== Buttons ===== */ +#bookmarks-toggle, +#theme-toggle, +#github-link, +.rss-button { + border: 1px solid rgba(255, 255, 255, 0.18); + background: rgba(255, 255, 255, 0.08); + color: white; + cursor: pointer; + transition: all var(--transition); + backdrop-filter: blur(10px); +} + +#bookmarks-toggle, +.rss-button { + padding: 0.75rem 1.2rem; + border-radius: 999px; + font-size: 0.95rem; + text-decoration: none; +} + +#theme-toggle, +#github-link { + width: 52px; + height: 52px; + border-radius: 50%; + font-size: 1.4rem; +} + +/* The anchor needs explicit centring that the button gets for free. */ +#github-link { + display: inline-flex; + align-items: center; + justify-content: center; + text-decoration: none; +} + +/* The logo is a grid item; the anchor wrapping it must not change that layout, + only make the image clickable. */ +.logo-link { + display: inline-flex; + align-items: center; + text-decoration: none; + border-radius: var(--radius-sm); +} + +.logo-link:focus-visible, +#github-link:focus-visible { + outline: 2px solid white; + outline-offset: 3px; +} + +#bookmarks-toggle:hover, +#theme-toggle:hover, +#github-link:hover, +.rss-button:hover { + background: rgba(255, 255, 255, 0.16); +} + +#bookmarks-toggle.active { + background: rgba(139, 92, 246, 0.35); + border-color: var(--brand-accent); +} + +/* ===== Stats ===== */ +.stats-bar { + max-width: 1440px; + margin: 1rem auto 0; + display: flex; + justify-content: center; + gap: 1.25rem; + flex-wrap: wrap; + font-size: 0.95rem; + color: rgba(255, 255, 255, 0.82); +} + +/* ===== Filters ===== */ +.filters { + background: var(--bg-secondary); + border-bottom: 1px solid var(--border-color); + padding: 0.8rem 1.5rem; +} + +.filter-section-title { + max-width: 1440px; + margin: 0 auto 0.5rem; + font-size: 0.82rem; + font-weight: 700; + text-transform: uppercase; + color: var(--text-secondary); +} + +.filter-scroll { + max-width: 1440px; + margin: 0 auto; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; +} + +.filter-pills { + display: flex; + gap: 0.5rem; + flex-wrap: nowrap; + padding-bottom: 0.25rem; +} + +.pill { + border: 1px solid var(--border-color); + background: var(--bg-primary); + color: var(--text-secondary); + padding: 0.5rem 0.9rem; + border-radius: 999px; + cursor: pointer; + white-space: nowrap; + font-size: 0.8rem; + flex-shrink: 0; + transition: all var(--transition); +} + +.pill:hover { + border-color: var(--brand-accent); +} + +.pill.active { + background: var(--brand-primary); + border-color: var(--brand-primary); + color: white; +} + +.pill .count { + margin-left: 0.4rem; + opacity: 0.85; +} + +/* ===== Main ===== */ +main { + max-width: 1440px; + margin: 0 auto; + padding: 1.5rem; +} + +/* ===== Controls ===== */ +.controls { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + flex-wrap: wrap; + margin-bottom: 1.25rem; +} + +.controls-right { + display: flex; + gap: 0.75rem; +} + +#date-filter, +#sort-by { + padding: 0.75rem 1rem; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + color: var(--text-primary); + border-radius: 12px; + min-width: 160px; +} + +/* ===== Loading ===== */ +.loading { + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1rem; + padding: 4rem 2rem; + color: var(--text-secondary); + font-size: 1rem; +} + +.loading.visible { + display: flex; +} + +.loading-spinner { + width: 40px; + height: 40px; + border: 3px solid var(--border-color); + border-top-color: var(--brand-accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* ===== No Results ===== */ +.no-results { + display: none; + text-align: center; + padding: 4rem 2rem; + color: var(--text-secondary); +} + +.no-results.visible { + display: block; +} + +/* ===== Grid ===== */ +.articles-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); + gap: 1rem; +} + +/* ===== Cards ===== */ +.article-card { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + padding: 1.2rem; + transition: all var(--transition); + /* Column flexbox so the Read More button can be pushed to the bottom of the + card with margin-top:auto, instead of floating wherever the summary happens + to end. */ + display: flex; + flex-direction: column; + /* Allow the card to shrink below its content's intrinsic width inside the + grid track; without this a long unbreakable token (URL, hash) widens the + track and overflows the page on Chrome mobile. */ + min-width: 0; +} + +.article-card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); + border-color: rgba(139, 92, 246, 0.3); +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 0.5rem; +} + +.blog-tag { + padding: 0.3rem 0.75rem; + border-radius: 999px; + font-size: 0.72rem; + font-weight: 700; +} + +.bookmark-btn { + background: none; + border: none; + font-size: 1.2rem; + cursor: pointer; + color: var(--text-secondary); + flex-shrink: 0; + align-self: flex-start; +} + +.bookmark-btn.bookmarked { + color: #f59e0b; +} + +.article-title { + font-size: 1.05rem; + font-weight: 700; + margin: 0.8rem 0 0.5rem; + overflow-wrap: anywhere; +} + +.article-title a { + color: var(--text-primary); + text-decoration: none; + transition: color var(--transition); +} + +.article-title a:hover { + color: var(--brand-accent); +} + +.article-meta { + display: flex; + gap: 0.5rem 1rem; + flex-wrap: wrap; + font-size: 0.8rem; + color: var(--text-tertiary); + margin-bottom: 0.75rem; + line-height: 1.4; + overflow-wrap: anywhere; +} + +.article-summary { + color: var(--text-secondary); + font-size: 0.92rem; + line-height: 1.55; + overflow-wrap: anywhere; + /* Explicit, so a full card (where margin-top:auto resolves to zero) still + keeps the button clear of the text. */ + margin-bottom: 1rem; +} + +/* ===== Toast ===== */ +.toast { + position: fixed; + bottom: 2rem; + left: 50%; + transform: translateX(-50%) translateY(1rem); + background: var(--bg-tertiary); + border: 1px solid var(--border-color); + color: var(--text-primary); + padding: 0.75rem 1.5rem; + border-radius: 999px; + opacity: 0; + pointer-events: none; + transition: all 0.25s ease; + z-index: 9999; +} + +.toast.visible { + opacity: 1; + transform: translateX(-50%) translateY(0); +} + +/* ===== Mobile ===== */ +@media (max-width: 768px) { + header { + padding: 1rem 1rem 1.25rem; + } + + .logo { + grid-template-columns: 1fr; + justify-items: center; + text-align: center; + gap: 0.75rem; + } + + .logo-spacer { + display: none; + } + + .site-logo { + width: 60px; + height: 60px; + } + + .logo h1 { + font-size: 2rem; + text-align: center; + } + + .logo .subtitle { + font-size: 0.95rem; + max-width: 320px; + } + + .header-actions { + margin-top: 1rem; + flex-direction: column; + align-items: center; + } + + .search-box { + width: 100%; + } + + .controls { + flex-direction: column; + align-items: stretch; + } + + .controls-right { + width: 100%; + } + + .controls-right select { + flex: 1; + min-width: unset; + } + + .articles-grid { + grid-template-columns: 1fr; + } + + .stats-bar { + gap: 0.75rem; + font-size: 0.85rem; + } +} + +@media (max-width: 480px) { + .logo h1 { + font-size: 1.75rem; + } + + .logo .subtitle { + font-size: 0.88rem; + } + + .search-box input { + font-size: 0.95rem; + padding: 0.85rem 1rem 0.85rem 2.75rem; + } + + .filter-pills { + gap: 0.4rem; + } + + .pill { + padding: 0.5rem 0.85rem; + font-size: 0.8rem; + } + + #bookmarks-toggle, + .rss-button { + width: 100%; + max-width: 420px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + } + + #theme-toggle { + align-self: center; + } + + .article-meta { + flex-direction: column; + gap: 0.35rem; + } +} + +/* ===== Light Theme ===== */ +[data-theme="light"] { + --bg-primary: #f6f8fa; + --bg-secondary: #ffffff; + --bg-tertiary: #eaeef2; + + --text-primary: #1c2128; + --text-secondary: #424a53; + --text-tertiary: #656d76; + + --border-color: #d0d7de; +} + +[data-theme="light"] .pill { + background: var(--bg-secondary); + color: var(--text-secondary); +} + +[data-theme="light"] .pill.active { + background: var(--brand-primary); + border-color: var(--brand-primary); + color: white; +} +/* --------------------------------------------------------------------------- + Full post viewer + + Only articles carrying a complete body (currently Microsoft Message Center + posts) get an expand button, because a Message Center permalink only opens + for someone with tenant admin access. Showing the post inline is the point. + Uses the native , so focus trapping and Escape come from the + platform. Both themes are driven by the existing custom properties. +--------------------------------------------------------------------------- */ + +.expand-btn { + /* Pinned to the bottom left of the card. Grid tracks already stretch cards in + a row to equal height, so with the card as a column flexbox `margin-top: + auto` eats the leftover space and every button lands on the same baseline + regardless of how long its summary runs. align-self stops it stretching to + the full card width. */ + margin-top: auto; + align-self: flex-start; + padding: 0.5rem 1rem; + font-size: 0.85rem; + font-weight: 600; + font-family: inherit; + color: var(--text-primary); + background: var(--bg-tertiary); + border: 1px solid var(--border-color); + border-radius: var(--radius-sm); + cursor: pointer; + transition: var(--transition); +} + +.expand-btn:hover { + border-color: var(--brand-accent); + color: var(--brand-accent); +} + +.expand-btn:focus-visible { + outline: 2px solid var(--brand-accent); + outline-offset: 2px; +} + +.article-dialog { + padding: 0; + border: none; + background: transparent; + max-width: min(760px, 92vw); + max-height: 88vh; + overflow: visible; +} + +.article-dialog::backdrop { + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(2px); +} + +.dialog-panel { + position: relative; + max-height: 88vh; + overflow-y: auto; + padding: 2rem; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + box-shadow: var(--shadow-lg); +} + +.dialog-close { + position: sticky; + top: 0; + float: right; + width: 2rem; + height: 2rem; + font-size: 1.4rem; + line-height: 1; + font-family: inherit; + color: var(--text-secondary); + background: var(--bg-tertiary); + border: 1px solid var(--border-color); + border-radius: 50%; + cursor: pointer; + transition: var(--transition); +} + +.dialog-close:hover { + color: var(--text-primary); + border-color: var(--brand-accent); +} + +.dialog-tags { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + margin-bottom: 1rem; +} + +.dialog-title { + margin: 0 0 0.75rem; + font-size: 1.4rem; + line-height: 1.35; + color: var(--text-primary); +} + +.dialog-text { + margin-top: 1.25rem; + color: var(--text-secondary); + line-height: 1.7; +} + +.dialog-text p { + margin: 0 0 1rem; +} + +.dialog-subheading { + margin: 1.5rem 0 0.75rem; + font-size: 1rem; + color: var(--text-primary); +} + +.dialog-links { + margin: 0; + padding-left: 1.2rem; + color: var(--text-secondary); +} + +.dialog-links li { + margin-bottom: 0.4rem; +} + +.dialog-links a, +.dialog-source-link { + color: var(--brand-accent); + overflow-wrap: anywhere; +} + +.dialog-source-link { + display: inline-block; + margin-top: 1.5rem; + padding-top: 1.25rem; + border-top: 1px solid var(--border-color); + font-size: 0.9rem; + font-weight: 600; +} + +@media (max-width: 600px) { + .dialog-panel { + padding: 1.25rem; + } + + .dialog-title { + font-size: 1.2rem; + } +} diff --git a/index.html b/index.html index 6cd0b79..15d744c 100644 --- a/index.html +++ b/index.html @@ -69,11 +69,19 @@