/*
 * The Orlando Connect — design system
 * ----------------------------------------------------------------------------
 * Added 2026-09-29 (orlandoconnect seat) against the refinement handoff.
 *
 * WHY THIS FILE EXISTS
 * The theme's visual language lived in inline style="" attributes across a 46 KB
 * front-page.php, a 21 KB page-directory.php and every other template. Nothing
 * could be changed in one place, so nothing was consistent: five different card
 * borders, four greys, headline case set on every h1-h6 globally.
 *
 * This file is the single place the palette, type scale, spacing and components
 * are defined. style.css is kept and still loads FIRST so nothing that depends on
 * the old tokens breaks; this file loads after and owns the new component layer.
 * Templates converted to these classes drop their inline styles as they go.
 *
 * Palette is the handoff's, not the old one:
 *   navy #101e35 · orange #f46b24 · text #17253a · muted #586579
 *   border #dde2e8 · surface #f5f6f8
 * Orange CTAs use DARK text (#101e35) — #fff on #f46b24 is 2.6:1 and fails.
 * #101e35 on #f46b24 is 7.9:1 and passes AA at every size we use.
 */

:root {
    --oc-navy:      #101e35;
    --oc-navy-soft: #1b2d4a;
    --oc-orange:    #f46b24;
    --oc-orange-dk: #d4550f;
    --oc-text:      #17253a;
    --oc-muted:     #586579;
    --oc-border:    #dde2e8;
    --oc-surface:   #f5f6f8;
    --oc-white:     #ffffff;

    /* Semantic status. Never color-only — every chip carries a word too. */
    --oc-ok:      #1c6b45;
    --oc-ok-bg:   #e8f5ee;
    --oc-warn:    #8a5a00;
    --oc-warn-bg: #fdf3e0;
    --oc-stop:    #9b1c1c;
    --oc-stop-bg: #fdeaea;
    --oc-info:    #1d4a7a;
    --oc-info-bg: #e9f1f8;

    --oc-display: 'Anton', 'Archivo Black', 'Oswald', 'Arial Narrow', sans-serif;
    --oc-body: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

    --oc-r: 6px;           /* one radius, used everywhere */
    --oc-r-sm: 4px;
    --oc-shadow: 0 1px 2px rgba(16,30,53,.05), 0 4px 12px rgba(16,30,53,.05);
    --oc-shadow-lift: 0 2px 4px rgba(16,30,53,.07), 0 10px 24px rgba(16,30,53,.09);

    --oc-gap: 24px;
    --oc-gap-lg: 40px;
    --oc-section: 56px;
    --oc-measure: 68ch;    /* 65-75 character reading measure */

    /* WIDTH SYSTEM (2026-09-29, "fullwidth like YouTube").
       Before: --oc-max was a flat 1200px, so every template stopped at a 1160px
       content box and a 1920px monitor showed 720px of empty margin. Below 1200px
       the site was already edge-to-edge, so only >=1200px viewports change here.

       The YouTube pattern, which is what these three tokens encode:
         --oc-max      content fills the viewport (100%), it is not a fixed box
         --oc-max-cap  ...but stops at 2400px so a 3440px ultrawide is not absurd
         --oc-read     text/form pages keep a human reading width, centered
       Chrome (header, footer, section backgrounds) is already full-bleed: those
       elements are the full-width parents and .oc-wrap is the inner box, so
       widening .oc-wrap is all that is needed.
       Gutters scale with the viewport (below) the way YouTube's do -- a 20px
       gutter that is right at 390px reads as no margin at all at 1920px. */
    --oc-gutter:  20px;
    --oc-max:     100%;
    --oc-max-cap: 2400px;
    --oc-read:    900px;

    /* Total height of the sticky site header: .oc-head__in's min-height plus the
       1px bottom border on .oc-head. Anything that pins itself to the top of the
       viewport has to clear this or it renders UNDERNEATH the header, which is
       z-index 900. .oc-head__in derives its min-height from this token so the two
       cannot drift apart. */
    --oc-head-h:  75px;
}

/* Wider viewport, wider gutter. Nothing else about the layout changes. */
@media (min-width: 1400px) { :root { --oc-gutter: 32px; } }
@media (min-width: 1800px) { :root { --oc-gutter: 48px; } }

/* ─── Reset-ish, scoped to what we own ─────────────────────────────────────── */

body.oc {
    font-family: var(--oc-body);
    color: var(--oc-text);
    background: var(--oc-white);
    font-size: 17px;
    line-height: 1.6;
    margin: 0;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

.oc *, .oc *::before, .oc *::after { box-sizing: border-box; }

/* The old stylesheet uppercases and Anton-ises every heading on the document.
   Inside .oc we take that back and apply display type deliberately, by class. */
.oc h1, .oc h2, .oc h3, .oc h4, .oc h5, .oc h6 {
    font-family: var(--oc-body);
    text-transform: none;
    letter-spacing: -0.015em;
    font-weight: 750;
    color: var(--oc-text);
    margin: 0 0 .4em;
    line-height: 1.2;
}
.oc h1 { font-size: clamp(2rem, 1.4rem + 2.4vw, 3rem); line-height: 1.1; }
.oc h2 { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem); }
.oc h3 { font-size: 1.3rem; }
.oc h4 { font-size: 1.1rem; }

/* The one expressive face, used for mastheads and lead headlines only. */
.oc-display {
    font-family: var(--oc-display);
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: .01em;
    line-height: 1.02;
}

.oc a { color: var(--oc-navy); text-decoration: none; }
.oc a:hover { color: var(--oc-orange-dk); text-decoration: underline; text-underline-offset: 2px; }
.oc p { margin: 0 0 1em; }
.oc img { max-width: 100%; height: auto; }

/* ─── Focus: visible, never removed ────────────────────────────────────────── */

.oc :focus-visible {
    outline: 3px solid var(--oc-orange);
    outline-offset: 2px;
    border-radius: var(--oc-r-sm);
}
/* Positioned at top:0 and translated out of view rather than parked at
   left:-9999px: a 9999px-offset element still counts toward the document's
   horizontal extent, which showed up as document overflow at 320-414 px. */
.oc-skip {
    position: fixed; left: 0; top: 0; z-index: 2000;
    transform: translateY(-120%);
    background: var(--oc-navy); color: #fff; padding: 12px 20px;
    font-weight: 700; border-radius: 0 0 var(--oc-r) 0;
}
.oc-skip:focus { transform: translateY(0); color: #fff; }

.oc-sr {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
    .oc *, .oc *::before, .oc *::after {
        animation-duration: .001ms !important; animation-iteration-count: 1 !important;
        transition-duration: .001ms !important; scroll-behavior: auto !important;
    }
}

/* ─── Layout ───────────────────────────────────────────────────────────────── */

.oc-wrap { max-width: min(var(--oc-max), var(--oc-max-cap)); margin: 0 auto; padding: 0 var(--oc-gutter); }
/* Reading/measure variants. These deliberately stay boxed and centered: full-bleed
   body copy is the thing the YouTube pattern does NOT do. --narrow is for forms,
   --read for article-length prose.
   The gutter is added to the cap rather than eaten out of it: box-sizing is
   border-box inside .oc, so a flat max-width:900px makes the reading column get
   NARROWER as --oc-gutter grows -- measured at 1920px it fell from 774px to 718px,
   the opposite of the intent. calc() keeps the content box fixed at any gutter. */
.oc-wrap--narrow { max-width: calc(800px + 2 * var(--oc-gutter)); }
.oc-wrap--read   { max-width: calc(var(--oc-read) + 2 * var(--oc-gutter)); }
.oc-section { margin: 0 0 var(--oc-section); }
.oc-stack > * + * { margin-top: var(--oc-gap); }

.oc-grid { display: grid; gap: var(--oc-gap); }
.oc-grid--2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.oc-grid--3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.oc-grid--4 { grid-template-columns: repeat(4, minmax(0,1fr)); }

/* A card grid going full width must ADD columns, not stretch three cards to 600px
   each -- that stretch is what makes a widened layout look broken rather than full.
   Gated at 1200px on purpose: below that the layout was ALREADY edge-to-edge, so the
   fixed column counts above stay exactly as they were at every width already reviewed
   (320/360/390/414/768/844/1024). Ungated, auto-fill also reflowed 1024px -- a nested
   2-up dropped to a single column there, a change nobody asked for.
   The min(100%, Npx) floor is load-bearing even here: a bare minmax(330px, 1fr)
   cannot shrink below 330px and would overflow any column narrower than that. */
@media (min-width: 1200px) {
    .oc-grid--2 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 440px), 1fr)); }
    .oc-grid--3 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr)); }
    .oc-grid--4 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); }
}
.oc-grid--main { grid-template-columns: minmax(0,1fr) 320px; gap: var(--oc-gap-lg); }

@media (max-width: 1000px) {
    .oc-grid--main { grid-template-columns: minmax(0,1fr); }
    .oc-grid--4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 720px) {
    .oc-grid--2, .oc-grid--3, .oc-grid--4 { grid-template-columns: minmax(0,1fr); }
    :root { --oc-section: 40px; --oc-gap-lg: 28px; }
}

/* ─── Section heading ──────────────────────────────────────────────────────── */

.oc-shead {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
    border-bottom: 2px solid var(--oc-navy);
    padding-bottom: 10px; margin-bottom: var(--oc-gap);
}
.oc-shead h2 { margin: 0; font-size: 1.35rem; letter-spacing: -.01em; }
.oc-shead__note { font-size: 13px; color: var(--oc-muted); margin: 0; }
.oc-shead__more { font-size: 13px; font-weight: 700; color: var(--oc-orange-dk); white-space: nowrap; }

/* ─── Eyebrow / kicker ─────────────────────────────────────────────────────── */

.oc-eyebrow {
    display: inline-block;
    font-size: 11px; font-weight: 800; letter-spacing: .1em;
    text-transform: uppercase; color: var(--oc-orange-dk);
}
.oc-eyebrow--muted { color: var(--oc-muted); }
.oc-eyebrow--navy  { color: var(--oc-navy); }

/* ─── Place label: real place first, masthead as the alias ─────────────────── */

.oc-place { font-size: 12px; color: var(--oc-muted); display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.oc-place__name { font-weight: 700; color: var(--oc-navy); }
.oc-place__alias { font-style: italic; }

/* ─── Buttons ──────────────────────────────────────────────────────────────── */

.oc-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    font-family: var(--oc-body); font-size: 15px; font-weight: 700;
    padding: 11px 20px; min-height: 44px;
    border-radius: var(--oc-r); border: 1.5px solid transparent;
    cursor: pointer; text-decoration: none !important;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.oc-btn--primary  { background: var(--oc-orange); color: var(--oc-navy) !important; border-color: var(--oc-orange-dk); }
.oc-btn--primary:hover  { background: var(--oc-orange-dk); color: #fff !important; }
.oc-btn--navy     { background: var(--oc-navy); color: #fff !important; border-color: var(--oc-navy); }
.oc-btn--navy:hover     { background: var(--oc-navy-soft); color: #fff !important; }
.oc-btn--ghost    { background: transparent; color: var(--oc-navy) !important; border-color: var(--oc-border); }
.oc-btn--ghost:hover    { border-color: var(--oc-navy); background: var(--oc-surface); }
.oc-btn--sm { font-size: 13px; padding: 7px 14px; min-height: 36px; }
.oc-btn[disabled], .oc-btn--disabled {
    background: var(--oc-surface); color: var(--oc-muted) !important;
    border-color: var(--oc-border); cursor: not-allowed;
}

/* ─── Status chips: word + colour, never colour alone ──────────────────────── */

.oc-chip {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
    padding: 3px 9px; border-radius: 999px; border: 1px solid transparent;
    white-space: nowrap;
}
.oc-chip--ok   { background: var(--oc-ok-bg);   color: var(--oc-ok);   border-color: #b9dfcb; }
.oc-chip--warn { background: var(--oc-warn-bg); color: var(--oc-warn); border-color: #ecd6a8; }
.oc-chip--stop { background: var(--oc-stop-bg); color: var(--oc-stop); border-color: #f0c2c2; }
.oc-chip--info { background: var(--oc-info-bg); color: var(--oc-info); border-color: #bcd4e8; }
.oc-chip--mute { background: var(--oc-surface); color: var(--oc-muted); border-color: var(--oc-border); }

/* ─── Cards ────────────────────────────────────────────────────────────────── */

.oc-card {
    background: var(--oc-white);
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-r);
    overflow: hidden;
    display: flex; flex-direction: column;
    transition: box-shadow .15s ease, border-color .15s ease, transform .15s ease;
}
.oc-card:hover { border-color: #c3ccd6; box-shadow: var(--oc-shadow-lift); }
.oc-card__body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.oc-card__title { font-size: 1.05rem; font-weight: 750; line-height: 1.28; margin: 0; }
.oc-card__title a { color: var(--oc-text); }
.oc-card__title a:hover { color: var(--oc-orange-dk); }
.oc-card__dek { font-size: .92rem; color: var(--oc-muted); margin: 0; line-height: 1.5; }
.oc-card__foot {
    margin-top: auto; padding-top: 10px; border-top: 1px solid var(--oc-border);
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    font-size: 12px; color: var(--oc-muted); flex-wrap: wrap;
}

/* ─── Media: fixed ratio boxes so nothing shifts on load ───────────────────── */

.oc-media { position: relative; display: block; background: var(--oc-surface); overflow: hidden; }
.oc-media--16x9 { aspect-ratio: 16 / 9; }
.oc-media--3x2  { aspect-ratio: 3 / 2; }
.oc-media--1x1  { aspect-ratio: 1 / 1; }
.oc-media > img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Posters carry essential text — show them whole on detail pages. */
.oc-media--contain > img { object-fit: contain; background: var(--oc-navy); }

.oc-media__label {
    position: absolute; left: 8px; bottom: 8px;
    background: rgba(16,30,53,.88); color: #fff;
    font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    padding: 3px 7px; border-radius: var(--oc-r-sm);
}

.oc-credit {
    font-size: 12px; color: var(--oc-muted); line-height: 1.45;
    padding: 6px 0 0; margin: 0;
}
.oc-credit--ai { color: var(--oc-warn); font-weight: 600; }

/* The honest no-photo panel. Plainly not a photograph. */
.oc-nophoto {
    background: var(--oc-surface);
    background-image: repeating-linear-gradient(135deg, rgba(16,30,53,.04) 0 8px, transparent 8px 16px);
    border-bottom: 1px solid var(--oc-border);
    display: flex; align-items: center; justify-content: center; text-align: center;
}
.oc-nophoto span {
    font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
    color: var(--oc-muted); padding: 10px 12px; line-height: 1.5;
}

/* ─── Lead story ───────────────────────────────────────────────────────────── */

.oc-lead { display: grid; grid-template-columns: minmax(0,1.25fr) minmax(0,1fr); gap: var(--oc-gap-lg); align-items: start; }
@media (max-width: 860px) {
    .oc-lead { grid-template-columns: minmax(0,1fr); gap: var(--oc-gap); }
    /* Photo first on a phone. The DOM order is text-then-photo so that the headline
       is the first thing a screen reader and a search crawler meet; visually the
       picture should lead, which is what `order` is for. */
    .oc-lead > :last-child { order: -1; }
}

.oc-lead__headline {
    font-size: clamp(1.9rem, 1.2rem + 2.6vw, 2.9rem);
    line-height: 1.06; font-weight: 800; letter-spacing: -.025em; margin: .25em 0 .35em;
}
.oc-lead__headline a { color: var(--oc-text); }
.oc-lead__headline a:hover { color: var(--oc-orange-dk); }
/* Excerpt only. The hero must never receive post_content. */
.oc-lead__dek { font-size: 1.08rem; line-height: 1.55; color: var(--oc-muted); max-width: 60ch; margin: 0 0 16px; }

/* ─── Shortlist ────────────────────────────────────────────────────────────── */

.oc-list { list-style: none; margin: 0; padding: 0; }
.oc-list > li { padding: 13px 0; border-bottom: 1px solid var(--oc-border); }
.oc-list > li:last-child { border-bottom: 0; }
.oc-list__title { font-size: .98rem; font-weight: 700; line-height: 1.32; margin: 3px 0 4px; }
.oc-list__title a { color: var(--oc-text); }
.oc-list__meta { font-size: 12px; color: var(--oc-muted); }

/* Shortlist row with the source publisher's own photograph. The thumbnail is a second
   link to the same story and is aria-hidden / tabindex=-1, so it adds a picture without
   adding a duplicate tab stop or a second announcement of the same headline. A row
   whose source published no photo keeps the plain single-column layout -- there is no
   placeholder tile, so a missing photo costs no space. */
.oc-list__row { display: block; }
.oc-list__row--media { display: flex; gap: 18px; align-items: flex-start; }
.oc-list__text { flex: 1; min-width: 0; }
/* 2026-09-30: the tile was a flat 104px, which reads as a stamp rather than a
   photograph now the page is full-width -- at 1440px the row is ~1000px of text beside
   a 104px picture, which is what made this section feel text-heavy and cramped. Fluid
   rather than a breakpoint ladder, so it tracks the column instead of stepping: 17vw
   between a 124px floor and a 240px ceiling. The floor is what keeps the text column
   readable on a phone; the ceiling stops the tile competing with the hero on an
   ultrawide. The source images carry it -- every one of the 360 published images is at
   least 400px wide and 200 are 1200px or more, so even a 240px tile on a 2x screen is
   asking for 480px from a source that has more. */
.oc-list__thumb {
    flex: 0 0 clamp(124px, 17vw, 240px); width: clamp(124px, 17vw, 240px);
    border-radius: var(--oc-r-sm);
    border: 1px solid var(--oc-border); overflow: hidden;
}
/* Below 480px the fluid width would eat the headline, so the tile is pinned small. */
@media (max-width: 480px) { .oc-list__thumb { flex: 0 0 108px; width: 108px; } }
/* A taller tile needs the row to breathe; text-only rows keep the tighter rhythm.
   Qualified with .oc-list > li because that selector would otherwise out-specify it. */
.oc-list > li.oc-list__row--media { padding: 18px 0; }

/* Event thumbnail: square, so it sits level with the date block opposite it. */
.oc-event__thumb {
    flex: 0 0 64px; width: 64px; border-radius: var(--oc-r-sm);
    border: 1px solid var(--oc-border); overflow: hidden; margin-top: 2px;
}

/* ─── Event row ────────────────────────────────────────────────────────────── */

.oc-event { display: flex; gap: 14px; align-items: flex-start; padding: 14px 0; border-bottom: 1px solid var(--oc-border); }
.oc-event:last-child { border-bottom: 0; }
.oc-event__date {
    flex: 0 0 54px; text-align: center; border-radius: var(--oc-r-sm);
    background: var(--oc-navy); color: #fff; padding: 7px 4px 8px; line-height: 1;
}
.oc-event__mon { font-size: 10px; font-weight: 800; letter-spacing: .1em; color: #a9c4e6; }
.oc-event__day { font-size: 22px; font-weight: 800; margin-top: 3px; }
.oc-event__body { flex: 1; min-width: 0; }
.oc-event__title { font-size: .98rem; font-weight: 700; line-height: 1.3; margin: 0 0 4px; }
.oc-event__title a { color: var(--oc-text); }
.oc-event__meta { font-size: 12.5px; color: var(--oc-muted); line-height: 1.5; display: flex; flex-wrap: wrap; gap: 4px 12px; }
.oc-event__meta span { display: inline-flex; gap: 4px; align-items: baseline; }

/* ─── Source box ───────────────────────────────────────────────────────────── */

.oc-source {
    border: 1px solid var(--oc-border); border-left: 4px solid var(--oc-navy);
    border-radius: var(--oc-r); background: var(--oc-surface);
    padding: 16px 18px; font-size: 14px; line-height: 1.6;
}
.oc-source h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .08em; color: var(--oc-muted); margin: 0 0 8px; font-weight: 800; }
.oc-source dl { display: grid; grid-template-columns: max-content minmax(0,1fr); gap: 4px 14px; margin: 0; }
.oc-source dt { font-weight: 700; color: var(--oc-muted); font-size: 13px; }
.oc-source dd { margin: 0; overflow-wrap: anywhere; }
.oc-source--ai { border-left-color: var(--oc-warn); background: var(--oc-warn-bg); }

/* ─── Article body: the reading measure ────────────────────────────────────── */

.oc-article__body { max-width: var(--oc-measure); font-size: 1.06rem; line-height: 1.7; }

/* Long unbroken tokens in body copy -- a pasted source URL is the usual one -- cannot
   wrap and push the document wider than the viewport. Found at 320px on articles
   during the 2026-09-29 fullwidth pass: a bare newsroom.ocfl.net link rendered 309px
   wide inside a 280px column for 30px of horizontal overflow. Pre-existing, not caused
   by the width change, but 320px is a tested width so it is fixed here.
   `anywhere` rather than `break-word` because break-word will still refuse to split a
   single token that has no break opportunity at all. */
.oc .entry-content, .oc-article__body { overflow-wrap: break-word; }
.oc .entry-content a, .oc-article__body a { overflow-wrap: anywhere; word-break: break-word; }
.oc-article__body p { margin: 0 0 1.15em; }
.oc-article__body h2 { font-size: 1.4rem; margin: 1.8em 0 .5em; }
.oc-article__body h3 { font-size: 1.15rem; margin: 1.6em 0 .4em; }
.oc-article__body ul, .oc-article__body ol { margin: 0 0 1.15em; padding-left: 1.3em; }
.oc-article__body li { margin-bottom: .4em; }
.oc-article__body a { color: var(--oc-navy); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--oc-orange); }
.oc-article__body hr { border: 0; border-top: 1px solid var(--oc-border); margin: 2em 0; }
.oc-article__body img { border-radius: var(--oc-r); }

.oc-byline { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 13.5px; color: var(--oc-muted); align-items: baseline; }
.oc-byline strong { color: var(--oc-text); font-weight: 700; }

/* ─── Forms ────────────────────────────────────────────────────────────────── */

.oc-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
.oc-field > label { font-size: 14px; font-weight: 700; color: var(--oc-text); }
.oc-field__hint { font-size: 12.5px; color: var(--oc-muted); margin: 0; }
.oc-input, .oc-select, .oc-textarea {
    font: inherit; font-size: 16px; /* 16px stops iOS zoom-on-focus */
    width: 100%; padding: 11px 13px; min-height: 44px;
    color: var(--oc-text); background: var(--oc-white);
    border: 1.5px solid var(--oc-border); border-radius: var(--oc-r);
}
.oc-input:focus, .oc-select:focus, .oc-textarea:focus { border-color: var(--oc-navy); }
.oc-textarea { min-height: 120px; resize: vertical; }
.oc-field--error .oc-input, .oc-field--error .oc-select, .oc-field--error .oc-textarea { border-color: var(--oc-stop); }
.oc-field__error { font-size: 13px; font-weight: 600; color: var(--oc-stop); display: flex; gap: 6px; align-items: baseline; overflow-wrap: anywhere; }
.oc-errsummary {
    border: 1.5px solid var(--oc-stop); background: var(--oc-stop-bg);
    border-radius: var(--oc-r); padding: 14px 18px; margin-bottom: 20px;
}
.oc-errsummary h2 { font-size: 15px; color: var(--oc-stop); margin: 0 0 8px; }
.oc-errsummary ul { margin: 0; padding-left: 20px; font-size: 14px; }
.oc-errsummary a { color: var(--oc-stop); text-decoration: underline; }

/* ─── Empty / no-result states ─────────────────────────────────────────────── */

.oc-empty {
    border: 1px dashed var(--oc-border); border-radius: var(--oc-r);
    background: var(--oc-surface); padding: 28px 24px; text-align: center;
    color: var(--oc-muted); font-size: 14.5px; line-height: 1.6;
}
.oc-empty strong { display: block; color: var(--oc-text); font-size: 16px; margin-bottom: 6px; }

/* ─── Pagination ───────────────────────────────────────────────────────────── */

.oc-pager { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; justify-content: center; margin: var(--oc-gap-lg) 0 0; }
.oc-pager a, .oc-pager span {
    min-width: 44px; min-height: 44px; padding: 0 12px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--oc-border); border-radius: var(--oc-r);
    font-size: 14px; font-weight: 700; color: var(--oc-navy);
}
.oc-pager a:hover { border-color: var(--oc-navy); background: var(--oc-surface); text-decoration: none; }
.oc-pager .current { background: var(--oc-navy); color: #fff; border-color: var(--oc-navy); }
.oc-pager .dots { border: 0; min-width: 20px; color: var(--oc-muted); }

/* ─── Ad slots ─────────────────────────────────────────────────────────────── */

.oc-ad { margin: 0 0 var(--oc-gap); }
.oc-ad__label {
    font-size: 10px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
    color: var(--oc-muted); margin: 0 0 5px; display: block;
}
.oc-ad__frame {
    border: 1px solid var(--oc-border); border-radius: var(--oc-r);
    background: var(--oc-surface); overflow: hidden;
    display: flex; align-items: center; justify-content: center;
}
/* Reserve height so a filled slot never pushes content down on load. */
.oc-ad--leaderboard .oc-ad__frame { min-height: 92px; }
.oc-ad--rail .oc-ad__frame        { min-height: 258px; }
.oc-ad--infeed .oc-ad__frame      { min-height: 132px; }
/* Empty slots collapse entirely rather than showing a giant empty box. */
.oc-ad--empty { display: none; }

/* House creative. Visually distinct from a paid placement on purpose: the frame is
   dashed and tinted, so a reader can tell at a glance that this is the publication
   talking about itself and not something an advertiser bought. */
.oc-ad--house .oc-ad__frame {
    border-style: dashed;
    background: linear-gradient(180deg, rgba(244,107,36,.05), rgba(16,30,53,.03));
    /* The min-height above reserves the box a PAID creative will occupy, so that
       creative arriving cannot shift the page. A house card is rendered inline by
       PHP -- there is nothing still to arrive -- so forcing it to the paid height
       just prints a tall empty rectangle with the copy stranded at the top and the
       button at the bottom. It takes its natural height instead. */
    min-height: 0;
}
.oc-ad__house {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px; flex-wrap: wrap; padding: 14px 18px; text-align: left;
}
.oc-ad__copy { flex: 1 1 260px; min-width: 0; }
.oc-ad__copy strong {
    display: block; color: var(--oc-navy);
    font-size: 15px; line-height: 1.3; margin-bottom: 2px;
}
.oc-ad__copy .oc-tiny { display: block; line-height: 1.45; }
/* In the 300px rail the button cannot share a line with the copy. */
.oc-ad--rail .oc-ad__house { flex-direction: column; align-items: flex-start; justify-content: center; }
/* flex-basis follows the MAIN axis, so the 260px basis that sets the copy's minimum
   WIDTH in the horizontal placements became a 260px minimum HEIGHT once the rail
   switched to column -- a ~90px card stretched to 343px with the button stranded at
   the bottom. In a column the copy sizes to its content. */
.oc-ad--rail .oc-ad__copy { flex: 0 0 auto; }
/* A full-bleed band placement between sections. */
.oc-ad-rail { margin-bottom: var(--oc-section); }

.oc-sponsored { font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--oc-warn); }

/* ─── Tables → cards on narrow screens ─────────────────────────────────────── */

.oc-tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--oc-border); border-radius: var(--oc-r); }
.oc-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.oc-table th, .oc-table td { padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--oc-border); }
.oc-table th { background: var(--oc-surface); font-weight: 800; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--oc-muted); }
.oc-table tr:last-child td { border-bottom: 0; }

/* ─── Utility ──────────────────────────────────────────────────────────────── */

.oc-muted { color: var(--oc-muted); }
.oc-small { font-size: 13px; }
.oc-tiny  { font-size: 12px; }
.oc-b     { font-weight: 700; }
.oc-center{ text-align: center; }
.oc-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.oc-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.oc-surface { background: var(--oc-surface); }
.oc-panel { background: var(--oc-surface); border: 1px solid var(--oc-border); border-radius: var(--oc-r); padding: 20px 22px; }
.oc-panel--navy { background: var(--oc-navy); border-color: var(--oc-navy); color: #e6ecf5; }
.oc-panel--navy h2, .oc-panel--navy h3 { color: #fff; }
.oc-panel--navy a { color: #ffd9c2; }

/* ══════════════════════════════════════════════════════════════════════════════
   SITE CHROME — header, navigation, community picker, footer
   ════════════════════════════════════════════════════════════════════════════ */

/* ─── Utility bar ──────────────────────────────────────────────────────────── */

.oc-util {
    background: var(--oc-navy); color: #b9c6d8;
    font-size: 12.5px; border-bottom: 1px solid rgba(255,255,255,.08);
}
.oc-util__in { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 38px; flex-wrap: wrap; }
.oc-util__facts { display: flex; align-items: center; gap: 8px 18px; flex-wrap: wrap; }
.oc-util__facts > * { display: inline-flex; align-items: center; gap: 6px; }
/* The live clock (assets/clock.js) sits inside the date group and ships [hidden].
   This rule is load-bearing, not cosmetic: the browser's own `[hidden]{display:none}`
   is a UA-stylesheet declaration, so ANY author `display` reaching this element wins
   over it and the empty <span> would take up layout before the first tick. Stating it
   here in the author sheet keeps it genuinely hidden until clock.js reveals it. */
.oc-util__facts [hidden] { display: none; }
.oc-clock { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.oc-util a { color: #fff; font-weight: 600; }
.oc-util a:hover { color: var(--oc-orange); }
.oc-util__links { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
@media (max-width: 720px) {
    .oc-util__links { display: none; }
    .oc-util__in { justify-content: center; }
}

/* ─── Masthead ─────────────────────────────────────────────────────────────── */

.oc-head { background: #fff; border-bottom: 1px solid var(--oc-border); position: sticky; top: 0; z-index: 900; }
.oc-head__in { display: flex; align-items: center; gap: 20px; min-height: calc(var(--oc-head-h) - 1px); }

.oc-brand { display: flex; align-items: center; gap: 12px; flex: 0 1 auto; min-width: 0; }
.oc-brand__mark { width: 40px; height: 40px; flex: 0 0 40px; border-radius: 8px; display: block; }

/* The supplied horizontal lockup. Width and height are both declared in the markup at
   the asset's own 3:1 ratio, so the header reserves its box before the PNG arrives and
   the nav beside it cannot be pushed on load. */
.oc-brand__logo { width: 150px; height: 50px; flex: 0 0 150px; display: block; object-fit: contain; }
/* The strapline explains; the lockup identifies. Beside the lockup it needs a divider
   and its own measure, or it reads as part of the mark. */
.oc-brand__strap--beside {
    display: block; max-width: 210px; line-height: 1.4; margin-top: 0;
    padding-left: 12px; border-left: 1px solid var(--oc-border);
    /* Two balanced lines. A ch-based width was measured against the strapline's own
       small font and came out near one word per line -- four fragments stacked beside
       a 50px lockup. A px cap sized to the lockup's height is the right constraint. */
    font-size: 11px; letter-spacing: .02em; text-wrap: balance;
}
@media (max-width: 1240px) { .oc-brand__strap--beside { display: none; } }
@media (max-width: 560px) { .oc-brand__logo { width: 124px; height: 41px; flex-basis: 124px; } }
.oc-brand__text { display: block; min-width: 0; }
.oc-brand__name {
    display: block;
    font-family: var(--oc-display); font-size: 25px; line-height: 1;
    text-transform: uppercase; letter-spacing: .01em; color: var(--oc-navy);
}
.oc-brand__strap { display: block; font-size: 11px; font-weight: 600; letter-spacing: .05em; color: var(--oc-muted); margin-top: 3px; }
/* The strapline is explanatory, not identifying. On a phone the masthead itself is
   the thing that has to fit, so the explanation steps aside. */
@media (max-width: 560px) {
    .oc-brand__strap { display: none; }
    .oc-brand__name { font-size: 19px; }
}
.oc-brand:hover { text-decoration: none; }
.oc-brand:hover .oc-brand__name { color: var(--oc-orange-dk); }

/* Four primary reader categories. Business paths are visually separate. */
.oc-nav { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.oc-nav__link {
    font-size: 14.5px; font-weight: 650; color: var(--oc-text);
    padding: 9px 13px; border-radius: var(--oc-r); white-space: nowrap;
    display: inline-flex; align-items: center; gap: 6px; min-height: 40px;
}
.oc-nav__link:hover, .oc-nav__link[aria-expanded="true"] { background: var(--oc-surface); color: var(--oc-navy); text-decoration: none; }
.oc-nav__link[aria-current="page"] { color: var(--oc-orange-dk); box-shadow: inset 0 -3px 0 var(--oc-orange); border-radius: var(--oc-r) var(--oc-r) 0 0; }
.oc-nav__caret { width: 9px; height: 9px; flex: 0 0 9px; transition: transform .15s ease; }
.oc-nav__link[aria-expanded="true"] .oc-nav__caret { transform: rotate(180deg); }

.oc-head__biz { display: flex; align-items: center; gap: 8px; padding-left: 16px; margin-left: 8px; border-left: 1px solid var(--oc-border); flex: 0 0 auto; }

.oc-burger {
    display: none; align-items: center; justify-content: center; gap: 7px;
    margin-left: auto; min-height: 44px; min-width: 44px; padding: 0 14px;
    background: var(--oc-navy); color: #fff; border: 0; border-radius: var(--oc-r);
    font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
.oc-burger__bars { display: inline-flex; flex-direction: column; gap: 3px; }
.oc-burger__bars i { display: block; width: 16px; height: 2px; background: #fff; border-radius: 2px; }

@media (max-width: 1080px) {
    .oc-nav, .oc-head__biz { display: none; }
    .oc-burger { display: inline-flex; }
    .oc-head__in { min-height: 64px; }
    .oc-brand__name { font-size: 21px; }
}

/* ─── Community picker (desktop dropdown) ──────────────────────────────────── */

.oc-pickwrap { position: relative; }
.oc-pick {
    position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
    width: min(720px, 90vw); background: #fff;
    border: 1px solid var(--oc-border); border-radius: var(--oc-r);
    box-shadow: var(--oc-shadow-lift); padding: 18px 20px 20px; z-index: 950;
}
.oc-pick[hidden] { display: none; }
.oc-pick__head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--oc-border); }
.oc-pick__head h2 { font-size: 14px; margin: 0; }
.oc-pick__grid { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 16px 22px; }
@media (max-width: 720px) { .oc-pick__grid { grid-template-columns: minmax(0,1fr); } }
.oc-pick__group h3 { font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--oc-muted); margin: 0 0 7px; }
.oc-pick__group ul { list-style: none; margin: 0; padding: 0; }
.oc-pick__group li { margin-bottom: 3px; }
.oc-pick__group a { display: block; padding: 5px 7px; margin-left: -7px; border-radius: var(--oc-r-sm); font-size: 13.5px; font-weight: 650; color: var(--oc-text); line-height: 1.35; }
.oc-pick__group a:hover { background: var(--oc-surface); color: var(--oc-navy); text-decoration: none; }
.oc-pick__alias { display: block; font-size: 11.5px; font-weight: 500; color: var(--oc-muted); font-style: italic; }

/* ─── Mobile drawer ────────────────────────────────────────────────────────── */

.oc-drawer { position: fixed; inset: 0; z-index: 1100; display: flex; justify-content: flex-end; }
.oc-drawer[hidden] { display: none; }
.oc-drawer__scrim { position: absolute; inset: 0; background: rgba(16,30,53,.55); border: 0; padding: 0; cursor: pointer; }
.oc-drawer__panel {
    position: relative; width: min(400px, 92vw); height: 100%;
    background: #fff; display: flex; flex-direction: column;
    box-shadow: -8px 0 30px rgba(16,30,53,.22);
}
.oc-drawer__top { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--oc-border); flex: 0 0 auto; }
.oc-drawer__close {
    min-width: 44px; min-height: 44px; border: 1px solid var(--oc-border); border-radius: var(--oc-r);
    background: #fff; color: var(--oc-navy); font: inherit; font-size: 20px; font-weight: 700; cursor: pointer; line-height: 1;
}
.oc-drawer__close:hover { background: var(--oc-surface); }
.oc-drawer__body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 8px 16px 32px; }
.oc-drawer__sec { padding: 14px 0; border-bottom: 1px solid var(--oc-border); }
.oc-drawer__sec:last-child { border-bottom: 0; }
.oc-drawer__sec > h2 { font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--oc-muted); margin: 0 0 8px; }
.oc-drawer__link { display: flex; align-items: center; min-height: 46px; padding: 6px 10px; margin: 0 -10px; border-radius: var(--oc-r); font-size: 16px; font-weight: 650; color: var(--oc-text); }
.oc-drawer__link:hover { background: var(--oc-surface); text-decoration: none; }
.oc-drawer__places { list-style: none; margin: 0; padding: 0; }
.oc-drawer__places a { display: block; padding: 9px 10px; margin: 0 -10px; border-radius: var(--oc-r); font-size: 14.5px; font-weight: 600; color: var(--oc-text); line-height: 1.35; }
.oc-drawer__places a:hover { background: var(--oc-surface); text-decoration: none; }
body.oc-locked { overflow: hidden; }

/* ─── Footer ───────────────────────────────────────────────────────────────── */

.oc-foot { background: var(--oc-navy); color: #b9c6d8; margin-top: var(--oc-section); font-size: 14px; }
.oc-foot a { color: #dbe4f0; }
.oc-foot a:hover { color: var(--oc-orange); }
/* Vertical padding only. A `padding:` shorthand here also sets left/right to 0,
   which wiped out the `padding: 0 var(--oc-gutter)` these elements inherit from
   .oc-wrap -- the footer columns then sat flush against the viewport edge while
   every section above them stayed inset. Longhands keep the gutter in one place. */
.oc-foot__top { padding-top: 44px; padding-bottom: 36px; display: grid; grid-template-columns: minmax(0,1.4fr) repeat(3, minmax(0,1fr)); gap: 32px; }
@media (max-width: 900px) { .oc-foot__top { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 560px) { .oc-foot__top { grid-template-columns: minmax(0,1fr); gap: 24px; } }
.oc-foot h2 { font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: #8b9cb5; margin: 0 0 12px; }
.oc-foot ul { list-style: none; margin: 0; padding: 0; }
.oc-foot li { margin-bottom: 8px; line-height: 1.4; }
.oc-foot__brand { font-family: var(--oc-display); font-size: 26px; text-transform: uppercase; color: #fff; line-height: 1; }
.oc-foot__about { max-width: 42ch; line-height: 1.6; margin: 12px 0 0; color: #9fb0c7; font-size: 13.5px; }
.oc-foot__bar { border-top: 1px solid rgba(255,255,255,.1); padding-top: 18px; padding-bottom: 26px; font-size: 12.5px; color: #8b9cb5; display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap; }


/* ─── Advertise panel ──────────────────────────────────────────────────────────
   Two columns on a wide screen, stacked below 760 px. Previously the grid was an
   inline style on the element with no breakpoint, so the CTA column was squeezed to
   a few characters wide on a phone. */
.oc-promo { display: grid; grid-template-columns: minmax(0,1.6fr) minmax(0,1fr); gap: 28px; align-items: center; padding: 32px; }
.oc-promo__actions { display: flex; flex-direction: column; gap: 10px; }
@media (max-width: 760px) {
    .oc-promo { grid-template-columns: minmax(0,1fr); gap: 20px; padding: 24px 20px; }
}

/* ─── Newsletter CTA ───────────────────────────────────────────────────────────
   Markup lives in hlme-core/inc/newsletter.php so the form, the double opt-in
   handler and the confirmed-subscriber count cannot drift apart. Only the skin is
   here. Dark surface, dark-on-orange button, 16px input so iOS does not zoom. */
.oc-news { background: var(--oc-navy); border-radius: var(--oc-r); padding: 24px 22px; color: #dbe4f0; }
.oc-news h2 { color: #fff; font-size: 1.25rem; margin: 0 0 8px; }
.oc-news p { font-size: 13.5px; line-height: 1.6; color: #b9c6d8; margin: 0 0 14px; }
.oc-news label { color: #fff; }
.oc-news .oc-input { background: #fff; border-color: transparent; }
.oc-news .oc-btn { width: 100%; }
.oc-news__note { font-size: 11.5px; color: #8b9cb5; margin: 12px 0 0; line-height: 1.5; }
.oc-news__note a, .oc-news p a { color: #ffd9c2; }
.oc-news__msg {
    background: var(--oc-warn-bg); color: var(--oc-warn); border: 1px solid #ecd6a8;
    border-radius: var(--oc-r-sm); padding: 10px 12px; font-size: 13.5px; font-weight: 600;
    margin: 0 0 14px;
}
/* Honeypot and the visually-hidden email label. Clipped, not parked off-canvas at
   -9999px, for the same document-extent reason as .oc-skip. */
.oc-news__hp, .oc-news .oc-sr {
    position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ─── Legacy component repairs ─────────────────────────────────────────────────
   These fix defects in components that predate the refinement and still render on
   templates not yet converted (page-advertise, page-directory, page-partner-*).
   Scoped under .oc so they apply wherever the new chrome is loaded, which is
   everywhere, without editing style.css out from under those templates. */

/* The advertise page's mail CTA is a 27-character address set in uppercase with
   0.08em tracking inside a non-wrapping inline-block: 352 px of unbreakable text in
   a 350 px column, which was the ONLY source of document overflow on that page at
   390 px. Long strings now wrap and break instead of pushing the document wide. */
.oc .hlme-btn-cta {
    max-width: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
    text-align: center;
}

/* ─── Connect TV ───────────────────────────────────────────────────────────── */

/* Click-to-play facade. The box holds its 16:9 height from first paint, so swapping
   the poster for the iframe on press cannot shift the page. */
.oc-video { position: relative; aspect-ratio: 16 / 9; background: var(--oc-navy); border-radius: var(--oc-r); overflow: hidden; }
.oc-video__btn {
    position: absolute; inset: 0; width: 100%; height: 100%;
    padding: 0; border: 0; background: none; cursor: pointer; display: block;
}
.oc-video__btn > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.oc-video__play {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: 68px; height: 48px; display: block;
    filter: drop-shadow(0 2px 6px rgba(0,0,0,.45));
    transition: transform .12s ease, opacity .12s ease;
    opacity: .92;
}
.oc-video__play > svg { width: 100%; height: 100%; display: block; }
.oc-video__btn:hover .oc-video__play,
.oc-video__btn:focus-visible .oc-video__play { transform: translate(-50%, -50%) scale(1.08); opacity: 1; }
.oc-video__btn:focus-visible { outline: 3px solid var(--oc-orange); outline-offset: -3px; }
.oc-video__frame { width: 100%; height: 100%; border: 0; display: block; }
/* A facade that has been pressed keeps the same box; only its contents changed. */
.oc-video--playing { background: #000; }

/* Featured video beside its playlist. Below 900px the rail becomes a horizontal
   scroller so it does not push the player off the top of the screen. */
.oc-tv { display: grid; grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr); gap: var(--oc-gap-lg); align-items: start; }
@media (max-width: 900px) { .oc-tv { grid-template-columns: minmax(0, 1fr); } }

.oc-tv__list { display: flex; flex-direction: column; gap: 10px; max-height: 420px; overflow-y: auto; padding-right: 4px; }
@media (max-width: 900px) { .oc-tv__list { max-height: none; } }

.oc-tv__item {
    display: flex; gap: 11px; align-items: flex-start; text-align: left;
    width: 100%; padding: 8px; border: 1px solid var(--oc-border);
    border-radius: var(--oc-r-sm); background: #fff; cursor: pointer;
    font: inherit; color: inherit;
}
.oc-tv__item:hover { background: var(--oc-surface); border-color: var(--oc-navy); }
.oc-tv__item:focus-visible { outline: 3px solid var(--oc-orange); outline-offset: 2px; }
.oc-tv__item[aria-current="true"] { border-color: var(--oc-orange); background: rgba(244,107,36,.06); }
/* 2026-09-30: 88px -> 112px. In a ~515px rail at 1440 the old thumb was a
   postage stamp, which is half of why these read as low-res; the other half was
   the 480px hqdefault source, fixed in orlando_connect_video.py. */
.oc-tv__thumb { flex: 0 0 112px; width: 112px; aspect-ratio: 16 / 9; border-radius: 4px; overflow: hidden; background: var(--oc-surface); }
.oc-tv__thumb > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.oc-tv__meta { flex: 1; min-width: 0; }
/* Both are <span>s inside a <span> (they sit in a <button>/<a>, where a <p> would be
   invalid), so they need to be told to be blocks or the channel name runs on from the
   end of the title as one sentence. */
.oc-tv__title { display: block; font-size: 13px; font-weight: 700; line-height: 1.3; margin: 0 0 3px; color: var(--oc-text); }
.oc-tv__chan { display: block; font-size: 11.5px; color: var(--oc-muted); }

/* Playlist grid on /connect-tv/ */
.oc-pl { border: 1px solid var(--oc-border); border-radius: var(--oc-r); overflow: hidden; background: #fff; }
.oc-pl__head { padding: 12px 14px; border-bottom: 1px solid var(--oc-border); background: var(--oc-surface); }
.oc-pl__name { font-size: 1rem; font-weight: 800; margin: 0; line-height: 1.25; }
.oc-pl__meta { font-size: 11.5px; color: var(--oc-muted); margin: 3px 0 0; }
.oc-pl__body { padding: 10px; display: flex; flex-direction: column; gap: 8px; }

/* ─── Article layout: reading column + ad rail ─────────────────────────────── */
/*
   2026-09-29 (orlandoconnect seat), after the full-width conversion in a4a58fa.

   THE PROBLEM, measured on staging before this change: the article page was a lone
   900px reading column centred in the viewport. At 1920px that left 1020px of empty
   white -- 53.1% of the screen -- and the page carried only two of the engine's five
   real placements (the header leaderboard, and an article_footer leaderboard centred
   inside that same narrow column, which is the "house ad floating in whitespace" the
   screenshot shows). There was no rail because, before a4a58fa, there was no width
   to put one in.

   WHAT DOES NOT CHANGE: the reading measure. --oc-read still sizes the main column,
   so the prose keeps the same 814px line length it had before. The space that was
   empty becomes a rail carrying real inventory -- it does not become a wider
   paragraph. Full-bleed body copy is the thing the YouTube pattern deliberately does
   NOT do, and the WIDTH SYSTEM note at the top of this file is explicit about it.

   WHY 1100px: below that a 300px rail would squeeze the main column under ~660px of
   content box. 1024 (a tested width) therefore stays single-column on purpose, and
   the rail stacks under the story rather than beside it.
*/
:root { --oc-rail: 300px; --oc-rail-gap: 32px; }
/* The in_feed_native creative is specced 336px wide; give it that at the widths where
   there is room, so the rail frames the ad instead of padding it with grey. */
@media (min-width: 1600px) { :root { --oc-rail: 336px; --oc-rail-gap: 40px; } }

/* The wrap has to be wide enough for BOTH columns. Gutter is added to the cap rather
   than eaten out of it, for the same border-box reason as .oc-wrap--read above. */
.oc-wrap--article {
    max-width: calc(var(--oc-read) + var(--oc-rail-gap) + var(--oc-rail) + 2 * var(--oc-gutter));
}

.oc-article-grid {
    display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--oc-gap-lg);
    /* BELOW the rail breakpoint the grid is one column, and .oc-wrap--article is
       wider than .oc-wrap--read was -- so without this cap the prose would simply
       stretch into the space the rail is not using yet. Measured at 1024px: the
       reading column went 814px -> 898px, about 95 characters, well past the 68ch
       --oc-measure target. Capping the grid keeps the single-column article at
       exactly the width it had before this change; only the rail breakpoint and
       above is new. */
    max-width: var(--oc-read); margin: 0 auto;
}
@media (min-width: 1100px) {
    .oc-article-grid {
        max-width: none; margin: 0;
        /* minmax(0, --oc-read) flexes UP TO the reading measure and no further, so
           between 1100px and the cap the main column shrinks instead of the rail
           overflowing. */
        grid-template-columns: minmax(0, var(--oc-read)) var(--oc-rail);
        gap: var(--oc-rail-gap);
    }
}
.oc-article-grid__main { min-width: 0; }
.oc-article-grid__rail { min-width: 0; }

/* Sticky is applied to the LAST rail unit, not to the whole rail. A rail taller than
   the viewport that is sticky as one block pins with its bottom off-screen and the
   reader can never reach it; a single short unit at the end cannot do that, and it
   gives the zone the behaviour its name (sticky_sidebar) describes.
   The offset clears the sticky header: at a flat top:16px the unit pinned at y=16
   and the header (z-index 900) painted over its "House promotion" label and first
   title line -- an ad slot half-hidden behind the masthead is not a placement an
   operator can sell. */
@media (min-width: 1100px) {
    .oc-rail__sticky { position: sticky; top: calc(var(--oc-head-h) + 16px); }
}

/* Rail headings sit tighter than section headings in the main column.
   Scoped .oc-rail <class> rather than the bare class: `.oc h2` (0,1,1) sets
   font-size: clamp(1.5rem .. 2rem) and text-transform: none, and a lone class
   selector (0,1,0) loses to it -- the heading rendered at 32px in the 336px rail.
   Same shape as .oc-foot h2 and .oc-drawer__sec > h2 above.

   font-size carries !important ONLY because the legacy style.css declares
   `h2 { font-size: 1.55rem !important }` at <=768px and `1.4rem !important` at
   <=480px. An !important element selector beats any non-important class selector at
   any specificity, so below 768px this heading computed to 22.4px -- measured, not
   assumed. The same legacy rule also inflates .oc-foot h2 and .oc-drawer__sec > h2
   (both declared 11px, both computing 22.4px on a phone today), so this is a
   sitewide defect with a sitewide fix -- dropping !important from those two legacy
   rules -- that is deliberately NOT done here: it moves heading sizes on every
   template at mobile width and needs its own verification pass. This one line keeps
   the rail correct without pretending to have fixed the root cause. */
.oc-rail .oc-rail__head {
    font-size: 12px !important;
    font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
    color: var(--oc-muted); margin: 0 0 10px; padding-bottom: 8px;
    border-bottom: 2px solid var(--oc-navy);
}
.oc-rail__block { margin-bottom: var(--oc-gap-lg); }
.oc-rail__block:last-child { margin-bottom: 0; }
/* In a 300px rail the 104px thumb leaves ~180px of text, which wraps to four or five
   words a line. Text-only rows read better at this width. */
.oc-rail .oc-list__thumb { display: none; }
.oc-rail .oc-list__title { font-size: .95rem; }

/* ─── Connect TV: independent creators ────────────────────────────────────── */
/*
   2026-09-29 (orlandoconnect seat), for the creator half of Connect TV.

   WHY A SEPARATE COMPONENT FROM .oc-pl
   .oc-pl is a playlist of an INSTITUTION's uploads: the box is the channel, and one
   line of meta is enough because "City of Sanford posts City of Sanford video" needs
   no argument. A creator card has to carry something an institution card does not —
   the EVIDENCE that this person is actually Central Florida based, quoted from their
   own site, with a link to where we read it. That evidence line is the component's
   reason to exist, not decoration, so it gets real space rather than being squeezed
   into .oc-pl__meta.

   HEADING SIZES ARE PINNED DELIBERATELY
   style.css:452-455 and :519 set bare `h2/h3/h4 { font-size: ... !important }` inside
   max-width:767px and max-width:430px media queries. !important beats any component
   class regardless of specificity, so a creator name set only by .oc-cr__name would
   silently jump to 1.2rem on a phone and wreck the card — which is exactly what
   already happens to .oc-pl__name. The mobile block at the end of this section
   re-asserts the component sizes with !important solely to defeat those legacy rules.
   Remove it only if those global rules go first.
*/

/* Creator card */
.oc-cr {
    border: 1px solid var(--oc-border); border-radius: var(--oc-r);
    background: #fff; overflow: hidden; display: flex; flex-direction: column;
}
.oc-cr__head {
    padding: 13px 15px; border-bottom: 1px solid var(--oc-border);
    background: var(--oc-surface);
}
.oc-cr__name { font-size: 1.05rem; font-weight: 800; margin: 0; line-height: 1.25; }
.oc-cr__name a { color: inherit; text-decoration: none; }
.oc-cr__name a:hover { color: var(--oc-orange-dk); text-decoration: underline; }
.oc-cr__sub {
    display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
    margin: 6px 0 0; font-size: 11.5px; color: var(--oc-muted);
}
.oc-cr__blurb { font-size: 12.5px; color: var(--oc-muted); margin: 7px 0 0; line-height: 1.45; }
.oc-cr__body { padding: 10px; display: flex; flex-direction: column; gap: 8px; flex: 1; }

/* The evidence line. Small, but it is the point of the card. */
.oc-cr__ev {
    margin: 0; padding: 9px 11px; border-top: 1px solid var(--oc-border);
    background: #fcfcfd; font-size: 11.5px; line-height: 1.5; color: var(--oc-muted);
}
.oc-cr__ev q { font-style: italic; }
.oc-cr__ev a { color: var(--oc-info); }

/* Tier chip. Never colour-only — the letter and a word are always present. */
.oc-tier {
    display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto;
    font-family: var(--oc-body); font-size: 10px; font-weight: 800;
    letter-spacing: .04em; text-transform: uppercase;
    padding: 2px 7px; border-radius: 999px; border: 1px solid transparent;
    white-space: nowrap;
}
.oc-tier--a { background: var(--oc-ok-bg);   color: var(--oc-ok);   border-color: rgba(28,107,69,.28); }
.oc-tier--b { background: var(--oc-warn-bg); color: var(--oc-warn); border-color: rgba(138,90,0,.28); }
.oc-tier--c { background: var(--oc-stop-bg); color: var(--oc-stop); border-color: rgba(155,28,28,.28); }

.oc-cr__cat {
    font-size: 10.5px; font-weight: 700; text-transform: uppercase;
    letter-spacing: .05em; color: var(--oc-navy);
}

/* Permission notice. This is a standing caveat, not an alert to be dismissed, so it
   is styled as a quiet informational panel rather than a warning banner. */
.oc-permit {
    border: 1px solid rgba(29,74,122,.25); background: var(--oc-info-bg);
    border-radius: var(--oc-r); padding: 14px 16px; margin: 0 0 var(--oc-gap);
}
.oc-permit h3 { font-size: .95rem; margin: 0 0 6px; color: var(--oc-info); }
.oc-permit p { margin: 0 0 6px; font-size: 12.5px; line-height: 1.55; color: var(--oc-text); }
.oc-permit p:last-child { margin-bottom: 0; }

/* Held-back (tier C) transparency list. */
.oc-held { border: 1px dashed var(--oc-border); border-radius: var(--oc-r); padding: 14px 16px; background: #fff; }
.oc-held > ul { margin: 10px 0 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.oc-held li { font-size: 12.5px; line-height: 1.5; color: var(--oc-muted); padding-left: 0; }
.oc-held b { color: var(--oc-text); }

@media (max-width: 767px) {
    /* Defeats style.css:452-455 / :519 bare-tag !important sizes. See note above. */
    .oc-cr__name { font-size: 1.02rem !important; line-height: 1.25 !important; }
    .oc-permit h3 { font-size: .95rem !important; line-height: 1.3 !important; }
}

/* ===========================================================================
   TOPIC FACETS — the "Things to do" category bar
   Added 2026-09-29 (orlandoconnect seat).

   These are LINKS, not buttons, and carry no JavaScript: each chip is a real
   URL (/things-to-do/?topic=<slug>) so a filtered view is shareable,
   bookmarkable, crawlable and works with JS off. The active chip is marked with
   aria-current="page", and the count is inside the link text rather than being
   a color-only or tooltip-only cue.

   Only topics with at least one UPCOMING event ever render a chip — see
   hlme_event_topic_counts(). There is no chip for an empty category.
   =========================================================================== */
.oc .oc-facets {
    margin: 0 0 22px;
    padding: 0 0 18px;
    border-bottom: 1px solid var(--oc-border);
}
.oc .oc-facets__label {
    font-family: var(--oc-label, 'Oswald', sans-serif);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--oc-muted);
    margin: 0 0 10px;
}
.oc .oc-facets__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.oc .oc-facet {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 7px 13px;
    border: 1px solid var(--oc-border);
    border-radius: 999px;
    background: var(--oc-white);
    color: var(--oc-text);
    font-size: 13px;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    transition: border-color .12s ease, background .12s ease;
}
.oc .oc-facet:hover {
    border-color: var(--oc-navy);
    background: var(--oc-surface);
    color: var(--oc-text);
}
.oc .oc-facet__n {
    font-size: 11px;
    font-weight: 700;
    color: var(--oc-muted);
    font-variant-numeric: tabular-nums;
}
/* Active facet: navy fill AND the word is still legible at AA on that fill.
   Never signalled by color alone — it also carries aria-current="page". */
.oc .oc-facet--on {
    background: var(--oc-navy);
    border-color: var(--oc-navy);
    color: #fff;
}
.oc .oc-facet--on:hover { background: var(--oc-navy-soft); border-color: var(--oc-navy-soft); color: #fff; }
.oc .oc-facet--on .oc-facet__n { color: #c6d2e2; }

/* Day divider on the events list.
   font-size carries !important because style.css sets `h2 { font-size:
   1.55rem !important }` below 767px and 1.4rem below 430px. A day label is not
   a section heading and must not inflate on a phone; the plain inline style the
   first version of this page used lost that fight silently. */
.oc .oc-daybar {
    font-family: var(--oc-label, 'Oswald', sans-serif);
    font-size: 1rem !important;
    line-height: 1.25 !important;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--oc-navy);
    margin: 26px 0 6px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--oc-border);
}
.oc .oc-daybar:first-child { margin-top: 0; }

/* Topic tags rendered on an individual story/event. */
.oc .oc-topics {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: baseline;
    margin: 0;
    padding: 0;
    list-style: none;
}
.oc .oc-topics__lead {
    font-size: 11px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--oc-muted);
    font-weight: 600;
}
.oc .oc-topic-tag {
    font-size: 12px;
    font-weight: 600;
    color: var(--oc-info);
    background: var(--oc-info-bg);
    border: 1px solid #bcd4e8;
    border-radius: var(--oc-r-sm);
    padding: 2px 8px;
    text-decoration: none;
}
.oc .oc-topic-tag:hover { background: #dbe8f4; color: var(--oc-info); }

@media (max-width: 767px) {
    .oc .oc-facets { padding-bottom: 14px; margin-bottom: 18px; }
    .oc .oc-facet { font-size: 12px; padding: 6px 11px; }
}

/* ═══════════════════════════════════════════════════════════════════
   HERO RAIL — the multi-story featured slider
   ═══════════════════════════════════════════════════════════════════

   Progressive enhancement, deliberately. Without JavaScript the rail is a
   plain vertical stack of five complete stories, each one reachable; the
   controls stay hidden because nothing would drive them. hero-slider.js adds
   .oc-hero--ready, which is what turns the stack into a scroll-snap carousel
   and reveals the controls. So a JS failure degrades to "all five stories
   visible", never to "one story and four hidden behind a dead button".

   Horizontal motion is native overflow scrolling with scroll-snap rather than
   a transform track: that gives real touch swipe, real momentum and real
   accessibility-tree order on every mobile browser for free, and the arrows
   become scrollBy() calls instead of a hand-rolled gesture layer. */

.oc .oc-hero { position: relative; }

/* No-JS / pre-JS: a readable stack, visibly separated. */
.oc .oc-hero__slide + .oc-hero__slide {
    margin-top: var(--oc-gap-lg);
    padding-top: var(--oc-gap-lg);
    border-top: 1px solid var(--oc-border);
}

/* JS present: one slide per view, snapped. */
.oc .oc-hero--ready .oc-hero__track {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
}
.oc .oc-hero--ready .oc-hero__track::-webkit-scrollbar { display: none; }

.oc .oc-hero--ready .oc-hero__slide {
    flex: 0 0 100%;
    min-width: 100%;
    scroll-snap-align: start;
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

/* Controls. Hidden until JS unhides them (the [hidden] attribute is removed by
   the script), so they never appear without behaviour behind them. */
.oc .oc-hero__controls {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 18px;
}
.oc .oc-hero__controls[hidden] { display: none; }

.oc .oc-hero__arrow {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    background: var(--oc-white);
    color: var(--oc-navy);
    border: 2px solid var(--oc-border);
    border-radius: var(--oc-r);
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.oc .oc-hero__arrow svg { width: 10px; height: 16px; }
.oc .oc-hero__arrow:hover { background: var(--oc-navy); border-color: var(--oc-navy); color: var(--oc-white); }
.oc .oc-hero__arrow:focus-visible { outline: 3px solid var(--oc-orange); outline-offset: 2px; }

.oc .oc-hero__dots {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.oc .oc-hero__dot {
    width: 30px;
    height: 6px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: var(--oc-border);
    cursor: pointer;
    transition: background .15s ease, transform .15s ease;
}
.oc .oc-hero__dot:hover { background: var(--oc-muted); }
.oc .oc-hero__dot[aria-current="true"] { background: var(--oc-orange); }
.oc .oc-hero__dot:focus-visible { outline: 3px solid var(--oc-orange); outline-offset: 3px; }

/* Touch targets: 6px of visible bar is right for the design and far too small
   to hit. The bar keeps its look; the hit area grows around it. */
@media (pointer: coarse) {
    .oc .oc-hero__dot { height: 10px; width: 34px; }
    .oc .oc-hero__arrow { width: 44px; height: 44px; }
}

@media (max-width: 480px) {
    .oc .oc-hero__controls { gap: 10px; }
    .oc .oc-hero__dot { width: 26px; }
}

/* Auto-rotation is a moving element the reader did not ask for, so it is the
   script's job to stop it under prefers-reduced-motion; this kills the snap
   animation itself for the manual controls too. */
@media (prefers-reduced-motion: reduce) {
    .oc .oc-hero--ready .oc-hero__track { scroll-behavior: auto; }
}
/* ═══════════════════════════════════════════════════════════════════
   APPROVED HOMEPAGE VISUAL DESIGN (2026-10-01)
   ═══════════════════════════════════════════════════════════════════

   The data layer for all three sections below was already correct and is NOT
   touched by this block: hero date-gating (oc_hero_slides / hlme_post_currency),
   neighborhood grouping (oc_mastheads_by_cluster) and the ConnectTV creator feed
   (hlme_connect_tv_creator_rail) all keep their existing behaviour. What was
   missing was the presentation Eddy approved in the mockup, which is what this is.

   SPECIFICITY NOTE, the same trap the ConnectTV v2 block documents at line 1289:
   design-system.css:106 sets `.oc h1 … .oc h6` (0-1-1), which beats any bare
   single class (0-1-0). Every heading rule here is therefore scoped under its
   block (`.oc-herolead .oc-feat__h`, 0-2-0) so it actually applies. Same for
   link colour: `.oc a` would otherwise repaint anything sitting on navy.
   ─────────────────────────────────────────────────────────────────── */

/* ── 1. Hero: feature + spotlight ───────────────────────────────────
   The mockup's two-column lead. The feature column is the EXISTING hero rail,
   unchanged in markup contract (hero-slider.js still finds .oc-hero,
   [data-oc-hero-track], .oc-hero__slide and its buttons) -- it is only given a
   column to live in and a photo-overlay treatment inside each slide.
   Single column below 1100px, which is where the article rail collapses too. */
.oc-herolead {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--oc-gap);
    margin-bottom: var(--oc-section);
}
@media (min-width: 1100px) {
    .oc-herolead { grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr); gap: var(--oc-gap-lg); }
}
.oc-herolead > .oc-hero { margin-bottom: 0; }

/* The slide itself becomes the mockup's photograph-led feature card: image
   full-bleed, headline over a navy scrim at the foot. */
/* Only in the no-JS stacked state. .oc-hero--ready resets this margin to 0 at
   line 1193, but that rule and this one are both 0-3-0 and this one is later, so
   it was winning in the carousel too -- every slide but the first sat 24px lower
   and, once the card fills its slide (see .oc-feat below), 24px shorter as well,
   which the carousel showed as a jump on every snap. Scoping it to the state it
   was written for is what the --ready reset was already trying to say. */
.oc .oc-herolead .oc-hero:not(.oc-hero--ready) .oc-hero__slide + .oc-hero__slide {
    margin-top: var(--oc-gap);
    padding-top: 0;
    border-top: 0;
}
.oc-feat {
    position: relative;
    display: block;
    border-radius: var(--oc-r);
    overflow: hidden;
    background: var(--oc-navy);
    isolation: isolate;
}
.oc-feat .oc-media,
.oc-feat .oc-nophoto {
    aspect-ratio: 16 / 10;
    margin: 0;
    border-radius: 0;
}
@media (max-width: 640px) { .oc-feat .oc-media, .oc-feat .oc-nophoto { aspect-ratio: 4 / 3; } }
/* A story with no source photograph still has to carry white overlay text, so
   the honest "no photo" tile goes navy here instead of its usual light surface
   -- otherwise the headline lands grey-on-grey. The wording is unchanged. */
/* The card's height came entirely from the photograph's aspect-ratio, because
   .oc-feat__body is positioned and so contributes nothing to it. At narrow widths
   the two move in OPPOSITE directions -- the 4/3 photo box gets shorter as the
   column narrows, while the headline wraps onto more lines and the body gets
   taller -- so below roughly 640px the body outgrew the card and the
   overflow:hidden above cut the top off it. Measured on staging at 390px wide:
   all five slides clipped, by 21px to 157px, which ate the place kicker and the
   first line or two of every headline.
   Stacking both children in a single grid cell makes the card take the height of
   whichever is taller, so the type can never be cut off at any width. Where the
   photograph is the taller of the two -- every slide at desktop width -- the
   result is identical to before, and align-self keeps the body at the foot. */
/* FOLLOW-UP to the stacking fix above (2026-10-01). Stacking both children in ONE
   cell stopped the clipping, but it traded one bug for a worse-looking one: the
   row then takes the height of whichever child is taller, and .oc-feat__body is
   NOT a scrim -- its own background is rgba(16,30,53,.96), i.e. effectively solid
   navy, for all but its top fade band. So on every width where the body is the
   taller child, the body is exactly as tall as the card and the photograph is
   painted over end to end. Measured on staging: 100% of the card covered at 390px
   on all five slides, and 71-93% at 1440px. The photograph is the whole point of
   a photograph-led feature card, so this is not acceptable at either width.

   Note the 1440px figure is PRE-EXISTING -- it measures identically on the commit
   before the stacking fix -- so the near-opaque body was always swallowing the
   desktop photo. The stacking fix only made the mobile case stop clipping; it did
   not cause the coverage.

   The fix is to stop making the photograph and the text compete for one cell. The
   photograph gets its own row and keeps its aspect-ratio height; the text block
   gets the row beneath it and is pulled back up by exactly the height of its own
   fade band, so the fade still lands on the foot of the picture and the type still
   starts where the backing is already solid. The card is then as tall as it needs
   to be rather than as tall as whichever child won, which means:
     - the photograph can never be covered by more than the fade band;
     - the body can never be clipped, at any width -- the card grows instead;
     - every contrast measurement in the block below still holds unchanged,
       because all type still sits on >= 0.96 navy.
   The overlap is a variable, not a literal, because it has to stay equal to the
   body's top padding -- the gradient stops below are pinned to that same number. */
.oc-feat {
    --oc-feat-fade: 56px;
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
}
/* height:100% rather than align-self:stretch: an item carrying aspect-ratio is
   NOT stretched by the grid, so without this the spare height handed to the 1fr
   row showed up as a band of bare navy between the photo and the text. The
   aspect-ratio still sets the row's natural size; this only cashes in the extra. */
.oc-feat > .oc-media,
.oc-feat > .oc-nophoto { grid-row: 1; grid-column: 1; height: 100%; }
.oc-feat > .oc-feat__body {
    grid-row: 2; grid-column: 1;
    position: relative;
    margin-top: calc(-1 * var(--oc-feat-fade));
}
@media (max-width: 640px) { .oc-feat { --oc-feat-fade: 44px; } }

/* The card height is now content-driven and so differs from slide to slide, but
   the track is a flex row whose slides all stretch to the tallest one -- which
   left a band of dead navy under every shorter card. Letting the card fill the
   slide, and giving the PHOTO row the spare height (it is the 1fr row), keeps the
   carousel one fixed height as it snaps, which is what it was before this change. */
.oc .oc-herolead .oc-hero__slide { display: flex; }
.oc .oc-herolead .oc-hero__slide > .oc-feat { flex: 1 1 auto; min-width: 0; }

.oc-feat .oc-nophoto { background: var(--oc-navy-soft); border: 0; }
.oc-feat .oc-nophoto span { color: rgba(255,255,255,.72); }

/* The scrim. Opaque enough at the foot that white type clears AA over any
   photograph, then clearing to nothing by mid-frame so the picture still reads. */
.oc-feat::after {
    content: "";
    /* A grid item in the PHOTO row, not an inset:0 overlay. Since the card now
       also contains a text row, inset:0 would stretch these stops over the whole
       card and tint the navy block underneath as well. */
    grid-row: 1; grid-column: 1;
    position: relative;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(to top,
        rgba(16,30,53,.70) 0%,
        rgba(16,30,53,.34) 38%,
        rgba(16,30,53,.10) 64%,
        rgba(16,30,53,0)   100%);
}
.oc-feat__body {
    position: absolute;
    z-index: 2;
    left: 0; right: 0; bottom: 0;
    padding: 56px 28px 24px;   /* the top 56px IS the fade; no type sits in it */
    color: var(--oc-white);
    /* Effectively solid behind the type, fading only in the top padding band.
       An earlier version faded across the whole box, which left the kicker --
       the FIRST line, so the highest one -- sitting on ~0.3 opacity over whatever
       the photograph was doing. Measured 1.70:1 against a bright sky. Small text
       on an arbitrary editorial photograph has no floor unless the backing does
       the work, and orange (#f46b24) is only 2.6:1 on white, so it can never
       survive a blown-out sky on its own. Over >=0.92 navy, orange measures
       7.9:1 and white 16.7:1, both of which hold on any photo. */
    /* Stops in PIXELS, not percentages, and matched to the top padding. A
       percentage fade compresses when the box is short -- a one-line headline
       with no dek makes the whole 0.55 band land on the kicker, which is why the
       place kicker still measured 3.01:1 over a bright photo after the first
       pass. Pinning the fade to the 56px padding band means type always starts
       exactly where the backing is already solid, whatever the headline length. */
    background: linear-gradient(to top,
        rgba(16,30,53,.96) 0,
        rgba(16,30,53,.96) calc(100% - 56px),
        rgba(16,30,53,0)   100%);
}
@media (max-width: 640px) {
    .oc-feat__body {
        padding: 44px 18px 16px;
        background: linear-gradient(to top,
            rgba(16,30,53,.96) 0,
            rgba(16,30,53,.96) calc(100% - 44px),
            rgba(16,30,53,0)   100%);
    }
}
.oc .oc-herolead .oc-feat__h {
    margin: 8px 0 10px;
    font-size: clamp(1.5rem, 1.05rem + 1.9vw, 2.45rem);
    line-height: 1.08;
    letter-spacing: -.015em;
    font-weight: 800;
    color: var(--oc-white);
    text-wrap: balance;
}
.oc .oc-herolead .oc-feat__h a { color: var(--oc-white); text-decoration: none; }
.oc .oc-herolead .oc-feat__h a:hover { text-decoration: underline; text-underline-offset: 3px; }
.oc-feat__dek {
    margin: 0 0 12px;
    max-width: 56ch;
    font-size: 1rem;
    line-height: 1.5;
    color: rgba(255,255,255,.88);
}
@media (max-width: 640px) { .oc-feat__dek { display: none; } }
/* The byline row inherits .oc-byline, which is tuned for dark-on-light. */
.oc-feat__body .oc-byline { margin-bottom: 14px; color: rgba(255,255,255,.80); }
.oc-feat__body .oc-byline strong { color: var(--oc-white); }
/* The place kicker sets colour on its CHILDREN -- .oc-place__name is navy
   (design-system.css:233) and .oc-place__alias inherits the muted grey from
   .oc-place. Recolouring the parent therefore does nothing: the children keep
   their own declarations and land navy-on-photograph. Measured at 1.00:1 before
   this rule. Each child has to be named. */
.oc-feat__body .oc-place,
.oc-feat__body .oc-place a,
.oc-feat__body .oc-place__name { color: var(--oc-orange); }
.oc-feat__body .oc-place__alias { color: rgba(255,255,255,.80); }
.oc-feat__credit {
    margin: 8px 0 0;
    font-size: 11.5px;
    line-height: 1.4;
    color: rgba(255,255,255,.62);
}
.oc .oc-feat__body .oc-btn--primary { background: var(--oc-orange); color: var(--oc-navy); }
.oc .oc-feat__body .oc-btn--primary:hover { background: #ff8343; color: var(--oc-navy); }

/* Spotlight: the mockup's right-hand card. Its story comes from the same gated
   pool the rail does (oc_hero_slides), one past the five the rail shows -- so it
   is a real, in-date, photographed story and never a repeat of a slide. */
.oc-spot {
    display: flex;
    flex-direction: column;
    background: var(--oc-surface);
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-r);
    padding: 24px;
}
.oc-spot__kicker {
    display: block;
    font-family: var(--oc-display);
    font-size: 12px;
    letter-spacing: .14em;
    text-transform: uppercase;
    /* NOT --oc-orange-dk. It measures 3.80:1 on --oc-surface and fails AA -- the
       same trap the ConnectTV v2 block hit on its own surface band. #b4460c is
       the same hue two steps darker and measures 5.08:1 here. */
    color: #b4460c;
    margin: 0 0 10px;
}
.oc .oc-spot__lede {
    margin: 0 0 18px;
    font-size: clamp(1.35rem, 1.1rem + .8vw, 1.72rem);
    line-height: 1.12;
    letter-spacing: -.015em;
    font-weight: 800;
    color: var(--oc-navy);
    text-wrap: balance;
}
.oc-spot__media { display: block; border-radius: var(--oc-r-sm); overflow: hidden; margin: 0 0 14px; }
.oc-spot__media .oc-media, .oc-spot__media .oc-nophoto { aspect-ratio: 3 / 2; }
.oc .oc-spot__h { margin: 0 0 8px; font-size: 1.12rem; line-height: 1.28; font-weight: 800; }
.oc .oc-spot__h a { color: var(--oc-navy); }
.oc .oc-spot__h a:hover { color: var(--oc-orange-dk); }
.oc-spot__dek { margin: 0 0 14px; font-size: 14.5px; line-height: 1.55; color: var(--oc-muted); }
.oc-spot__foot { margin-top: auto; padding-top: 16px; border-top: 1px solid var(--oc-border); }
.oc-spot__meta {
    display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center;
    margin: 0 0 14px; font-size: 12px; color: var(--oc-muted);
}
.oc-spot__credit { margin: 6px 0 0; font-size: 11.5px; line-height: 1.4; color: var(--oc-muted); }

/* ── 2. Neighborhood photo cards ────────────────────────────────────
   Was six text-only boxes. Now the mockup's photographed community cards.
   Every photograph is a real documentary picture of a real place inside that
   cluster, from Wikimedia Commons, credited in-page and recorded with its
   licence in assets/neighborhoods/CREDITS.md. No stock, no generative art --
   same rule the directory cards follow when they refuse to show a stock photo
   next to a named business (front-page.php, "Deliberately no image"). */
.oc-nb-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--oc-gap);
}
@media (min-width: 680px)  { .oc-nb-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1040px) { .oc-nb-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.oc-nb-card {
    display: flex;
    flex-direction: column;
    background: var(--oc-white);
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-r);
    overflow: hidden;
    box-shadow: var(--oc-shadow);
    transition: box-shadow .16s ease, transform .16s ease;
}
.oc-nb-card:hover { box-shadow: var(--oc-shadow-lift); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .oc-nb-card:hover { transform: none; } }

.oc-nb-card__fig { position: relative; margin: 0; aspect-ratio: 3 / 2; background: var(--oc-surface); overflow: hidden; }
.oc-nb-card__fig > img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The colored badge from the mockup. Decorative-only palette: these six carry
   no status meaning (unlike --oc-ok / --oc-warn / --oc-stop) and must never be
   reused as one. Each is >= 7.6:1 against the white text it holds, and the
   badge always carries the county NAME, so the colour is never the only signal. */
.oc-nb-card__badge {
    position: absolute;
    top: 12px; left: 12px;
    z-index: 2;
    display: inline-block;
    padding: 5px 11px;
    border-radius: var(--oc-r-sm);
    background: var(--oc-nb-c, #10335c);
    color: var(--oc-white);
    font-family: var(--oc-display);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    line-height: 1.25;
}
.oc-nb-card__count {
    position: absolute;
    right: 12px; bottom: 12px;
    z-index: 2;
    padding: 4px 10px;
    border-radius: var(--oc-r-sm);
    background: rgba(16,30,53,.86);
    color: var(--oc-white);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .04em;
}
.oc-nb-card__body { padding: 16px 18px 18px; display: flex; flex-direction: column; flex: 1; }
.oc .oc-nb-card__h { margin: 0 0 8px; font-size: 1.12rem; line-height: 1.25; font-weight: 800; color: var(--oc-navy); }
.oc .oc-nb-card__h a { color: inherit; }
.oc .oc-nb-card__h a:hover { color: var(--oc-orange-dk); }
.oc-nb-card__places { margin: 0 0 12px; font-size: 13.5px; line-height: 1.5; color: var(--oc-muted); }
.oc-nb-card__cred {
    margin-top: auto; padding-top: 10px;
    border-top: 1px solid var(--oc-border);
    font-size: 11px; line-height: 1.45; color: var(--oc-muted);
}
.oc-nb-card__cred a { color: var(--oc-muted); text-decoration: underline; text-underline-offset: 2px; }
.oc-nb-card__cred a:hover { color: var(--oc-info); }

.oc-nb-orange   { --oc-nb-c: #10335c; }   /* Orange County   — clusters 1, 2, 3 */
.oc-nb-seminole { --oc-nb-c: #1c5e3f; }   /* Seminole County — cluster 4 */
.oc-nb-osceola  { --oc-nb-c: #8a3b12; }   /* Osceola County  — cluster 5 */
.oc-nb-lakepolk { --oc-nb-c: #5b2d6b; }   /* Lake & Polk     — cluster 6 */

/* ── 3. ConnectTV: dark-navy branded grid ───────────────────────────
   Was a single embedded player with a sidebar list -- a bare YouTube widget.
   Now the branded band + card grid the /connect-tv/ page already uses, so the
   homepage strip and the landing page are visibly the same product. Classes are
   deliberately NOT .oc-ctv-*: that block is scoped to one template and sized
   2-up, and the homepage wants 3-up inside a full-bleed navy band. The card
   interior mirrors it on purpose. */
.oc-ctvhome {
    background: var(--oc-navy);
    color: var(--oc-white);
    border-radius: var(--oc-r);
    margin: 0 0 var(--oc-section);
    padding: 36px 32px 32px;
}
@media (max-width: 680px) { .oc-ctvhome { padding: 26px 20px 24px; border-radius: var(--oc-r-sm); } }
.oc-ctvhome__head {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    gap: 10px var(--oc-gap);
    margin: 0 0 var(--oc-gap);
}
.oc-ctvhome__kicker {
    display: inline-block;
    padding: 4px 10px;
    border-radius: var(--oc-r-sm);
    background: var(--oc-orange);
    color: var(--oc-navy);           /* white on orange is 2.6:1 and fails AA */
    font-family: var(--oc-display);
    font-size: 11.5px; font-weight: 700;
    letter-spacing: .14em; text-transform: uppercase;
    margin: 0 0 12px;
}
.oc .oc-ctvhome__h {
    margin: 0;
    font-family: var(--oc-display);
    font-size: clamp(1.9rem, 1.3rem + 2.6vw, 3.1rem);
    line-height: .96;
    letter-spacing: .005em;
    text-transform: uppercase;
    color: var(--oc-white);
}
.oc-ctvhome__note { margin: 0; flex: 1 1 260px; font-size: 14px; line-height: 1.5; color: rgba(255,255,255,.76); }
.oc .oc-ctvhome__more {
    margin-left: auto; align-self: flex-end;
    font-size: 13.5px; font-weight: 700; white-space: nowrap;
    color: var(--oc-orange);
}
.oc .oc-ctvhome__more:hover { color: #ff8343; text-decoration: underline; }

.oc-ctvhome__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--oc-gap);
    margin: 0 0 var(--oc-gap);
}
@media (min-width: 680px)  { .oc-ctvhome__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1040px) { .oc-ctvhome__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.oc-ctvhome__card {
    display: flex; flex-direction: column;
    background: var(--oc-white);
    border-radius: var(--oc-r);
    overflow: hidden;
    transition: box-shadow .16s ease, transform .16s ease;
}
.oc-ctvhome__card:hover { box-shadow: 0 12px 28px rgba(0,0,0,.35); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .oc-ctvhome__card:hover { transform: none; } }

.oc-ctvhome__poster {
    position: relative; display: block;
    aspect-ratio: 16 / 9;
    background: var(--oc-navy-soft);
    overflow: hidden;
    border: 0; padding: 0; width: 100%;
    cursor: pointer;
}
.oc-ctvhome__poster > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.oc-ctvhome__poster:focus-visible { outline: 3px solid var(--oc-orange); outline-offset: -3px; }
.oc-ctvhome__play {
    position: absolute; right: 10px; bottom: 10px;
    width: 46px; height: 32px; z-index: 2;
    pointer-events: none;
}
.oc-ctvhome__play > svg { width: 100%; height: auto; display: block; }
.oc-ctvhome__cat {
    position: absolute; top: 10px; left: 10px; z-index: 2;
    padding: 4px 9px; border-radius: var(--oc-r-sm);
    background: var(--oc-orange); color: var(--oc-navy);
    font-family: var(--oc-display);
    font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}
.oc-ctvhome__body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.oc .oc-ctvhome__title { margin: 0; font-size: 1rem; line-height: 1.3; font-weight: 800; color: var(--oc-navy); }
.oc .oc-ctvhome__title a { color: inherit; }
.oc .oc-ctvhome__title a:hover { color: var(--oc-orange-dk); text-decoration: underline; }
.oc-ctvhome__chan { margin: 0; font-size: 13px; color: var(--oc-muted); }
.oc-ctvhome__chan strong { color: var(--oc-text); font-weight: 700; }

/* The disclosure. Unchanged wording -- it is the honest statement of the
   permission position and is not softened by being restyled. Held at AA on
   navy: rgba(255,255,255,.82) over #101e35 measures ~11:1. */
.oc-ctvhome__disclose {
    margin: 0;
    max-width: 92ch;
    font-size: 12.5px;
    line-height: 1.6;
    color: rgba(255,255,255,.82);
}
.oc .oc-ctvhome__disclose a { color: var(--oc-orange); text-decoration: underline; text-underline-offset: 2px; }
.oc .oc-ctvhome__disclose a:hover { color: #ff8343; }
