﻿/* Scene Board.
 *
 * Ported from docs/storyboard-handoff/public/scene-board.css. Everything is
 * namespaced under .sb- so it cannot reach the rest of the dashboard.
 *
 * Two deliberate changes from the handoff:
 *
 *   - Archivo is gone. The board uses Cabin Condensed, the site font.
 *   - The accent was #ec3013. Remapped to the site gold, with the shared
 *     danger red kept for errors only.
 *
 * .sb-host is new and load-bearing. .page is display:block with no height
 * (css/dashboard.css), so .sb-board's height:100% flex column collapses to
 * nothing and the board renders blank without it.
 */

/* Sized off --header-height (css/global.css) rather than a measured constant,
   so the board follows the site header instead of drifting when it changes.
   Same shape as the .content rule in global.css:283.
   Note the editor sets --header-height to 0 (editor/css/pr-editor.css), which
   is correct there: it runs full screen with no site header. */
.sb-host {
    display: flex;
    height: calc(100vh - var(--header-height) - 20px);
    min-height: 420px;
}

/* For a board embedded in a container that ALREADY HAS A DEFINITE HEIGHT --
   a panel on an idea page, say.

   Not a general replacement for the rule above. A percentage height resolves
   against the parent's height, and on the dashboard there is none to resolve
   against: .content is `flex: 1` with no height and .page is a plain block with
   only padding, so this collapses to zero and the board renders blank. That was
   the original bug this file was written to fix.

   Use it only where you control the parent's height, and check it in place.

     <div class="sb-host sb-host-fill"></div>
*/
.sb-host-fill {
    height: 100%;
    min-height: 0;
}

/* The Scene Board pane inside the editor (pages/editscript.php).
 *
 * Same shape as #tutorialDiv in editor/css/tutorial.css: a full-height pane of
 * .editorMain, which has margins but no height of its own. --header-height is
 * already 0px in the editor (editor/css/pr-editor.css), so this resolves to the
 * viewport and needs nothing measured. The root inside carries .sb-host-fill,
 * the variant written for exactly this -- a parent whose height is definite.
 *
 * No position or z-index here on purpose. The reason a pane in this stack would
 * want them is .editorMain > .fixed-wrapper, which is position:fixed across the
 * viewport at z-index 100 and shows the script header and footer through
 * anything merely later in the document. showSceneBoard() hides that wrapper
 * instead, which removes the cause rather than out-stacking it -- otherwise an
 * invisible fixed layer stays over the board, and anything added to it later
 * bleeds through again. */
#sceneBoardDiv {
    margin-top: 7px;
    height: calc(100vh - var(--header-height) - 8px);
}

/* Overlay variant: covers the parent completely, for a board that sits on top
   of the script or title-page editor rather than beside it. */
.sb-host-overlay {
    position: absolute;
    inset: 0;
    height: auto;
    min-height: 0;
    z-index: 20;
    background: var(--sb-bg, #f3f2f2);
}

/* One palette, both roots.
 *
 * .sb-list is not inside .sb-board -- a board and the listing are alternative
 * renders of the same mount, siblings rather than parent and child. So anything
 * shared between them, .sb-readonly most of all, found these undefined on the
 * list: var() with no fallback makes the whole declaration invalid, which cost
 * that tag its colour and its border entirely. */
.sb-board,
.sb-list {
    --sb-ink: #201e1d;
    --sb-bg: #f3f2f2;
    --sb-surface: #fff;
    --sb-rule: #201e1d;
    --sb-line: #c9c5c3;
    --sb-muted: #6b6664;
    --sb-accent: #c9a84c;
    --sb-accent-dark: #ad8f39;
    --sb-accent-tint: #f7f0dc;
    --sb-danger: #e85347;
}

.sb-board {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    width: 100%;
    background: var(--sb-bg);
    color: var(--sb-ink);
    font-family: 'Cabin Condensed', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

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

.sb-loading {
    padding: 40px;
    font-size: 14px;
    color: #6b6664;
}

/* header
 *
 * One row on a laptop. Two things keep it there: the pickers stack their label
 * above the select rather than beside it, and the title truncates instead of
 * pushing. nowrap is deliberate -- letting this wrap is what doubled its height.
 */
.sb-head {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-end;
    gap: 16px;
    padding: 10px 20px;
    border-bottom: 2px solid #201e1d;
    background: #fff;
    flex: none;
}

/* Takes the slack, and gives it back first. min-width:0 is what allows a flex
   item to shrink below its content -- without it a long title cannot ellipsis
   and shoves everything else off the row.
   Stacked in two lines to match the label-above-select pickers beside it, so
   the gap matches theirs exactly. */
.sb-brand {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
}

/* Title and its pencil on one line. min-width:0 so the title can still shrink
   past its content and ellipsis -- without it a long name pushes the pencil
   off the row instead of truncating. */
.sb-title-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    max-width: 100%;
    min-width: 0;
}

.sb-title {
    max-width: 100%;
    min-width: 0;
    font-size: 18px;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The title of a script-built board doubles as the way into the script. No
   underline at rest -- it is the page's heading first and a link second, and a
   permanently underlined 18px title reads as a mistake. */
.sb-title-link {
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    transition: color .12s ease;
}

.sb-title-link:hover,
.sb-title-link:focus-visible {
    color: var(--sb-accent-dark);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Quiet, like the plus in a beat header: it sits beside the title permanently,
   and at full weight it would compete with the one thing the header is for. */
.sb-rename {
    flex: none;
    align-self: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sb-line);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    transition: color .12s ease;
}

/* Approaching the title brings it up, the way a beat brings up its plus.
   Scoped to the title row rather than the whole header, so crossing the header
   to reach a picker does not light it.

   The second rule is not redundant: an ancestor-hover selector outweighs the
   plain :hover below it, so without it the pencil would stay muted at the one
   moment it is actually being pointed at. Same shape as .sb-beat:hover .sb-add. */
.sb-title-row:hover .sb-rename {
    color: var(--sb-muted);
}

.sb-title-row:hover .sb-rename:hover,
.sb-rename:hover,
.sb-rename:focus-visible {
    color: var(--sb-accent-dark);
}

/* Delete. Same quiet-until-approached treatment as the pencil, but it resolves
   to the danger red rather than the accent -- the one control here that cannot
   be undone should not look like the ones that can. */
.sb-delete-board {
    flex: none;
    align-self: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sb-line);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    transition: color .12s ease;
}

.sb-title-row:hover .sb-delete-board {
    color: var(--sb-muted);
}

/* Ancestor-hover outranks a plain :hover, so the combined selector is what lets
   it turn red at the moment it is actually pointed at. */
.sb-title-row:hover .sb-delete-board:hover,
.sb-delete-board:hover,
.sb-delete-board:focus-visible {
    color: var(--sb-danger);
}

.sb-brand-sub {
    display: flex;
    align-items: center;
    gap: 8px;
    max-width: 100%;
}

/* Same size and tracking as .sb-field-label, so the second line of the brand
   and the labels on the pickers read as one row of small caps. */
.sb-kicker {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--sb-accent-dark);
    line-height: 1;
    white-space: nowrap;
}

.sb-readonly {
    flex: none;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--sb-muted);
    border: 1px solid var(--sb-line);
    padding: 1px 6px;
    line-height: 1.3;
}

/* Label above control. */
.sb-field {
    flex: none;
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.sb-field-label {
    font-size: 10px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--sb-muted, #6b6664);
    line-height: 1;
}

.sb-select {
    font: 500 13px 'Cabin Condensed', sans-serif;
    padding: 5px 8px;
    min-width: 0;
    width: 190px;
    max-width: 100%;
    border: 2px solid var(--sb-rule);
    border-radius: 0;
    background: var(--sb-surface);
    color: var(--sb-ink);
}

/* The structure picker carries the long framework names; these two are short. */
.sb-select-layout {
    width: 130px;
}

.sb-select-density {
    width: 120px;
}

/* Below a laptop there is not enough width for one row however tight the
   controls are, so let it wrap rather than crush the selects. */
@media (max-width: 900px) {
    .sb-head {
        flex-wrap: wrap;
    }

    .sb-brand {
        flex: 1 0 100%;
    }
}

/* Sits on the brand's second line, so it matches the kicker rather than
   competing with it. */
.sb-count {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--sb-muted);
    line-height: 1;
    white-space: nowrap;
}

.sb-count b {
    font-size: 11px;
    letter-spacing: 0;
    color: var(--sb-ink);
}

/* Bullet as generated content: it belongs to the join between the two, not to
   either of them, and nothing stray ends up in the DOM. */
.sb-brand-sub .sb-count::before {
    content: '\2022';
    margin-right: 7px;
    font-weight: 400;
    color: var(--sb-line);
}

/* Paired with .solid-btn (css/global.css, loaded on every page -- it used to be
   .blog-new-btn in css/blog.css, which is not loaded in the editor). This adds
   only what .solid-btn leaves out: it was written for an <a>, so a <button>
   still brings its own border and cursor. */
.sb-nav-btn {
    flex: none;
    align-self: flex-end;
    border: 0;
    cursor: pointer;
    font-family: 'Cabin Condensed', sans-serif;
}

/* Secondary: same shape, outlined. Two filled buttons side by side both read as
   the main action, which leaves neither of them reading as one. */
.sb-nav-btn-alt {
    background: transparent;
    color: var(--primary, #6576ff);
    box-shadow: inset 0 0 0 1px var(--primary, #6576ff);
}

.sb-nav-btn-alt:hover {
    background: var(--primary, #6576ff);
    color: #fff;
    filter: none;
}

/* The handoff's own square button style is gone -- every button in the widget
   now wears the site's, via .solid-btn + .sb-nav-btn above. */
.sb-board :focus-visible {
    outline: 2px solid var(--sb-accent-dark);
    outline-offset: 2px;
}

/* The body holds the holding group and the structure side by side. Which way
   round, and which axis, is the layout's business. */
.sb-body {
    flex: 1;
    min-height: 0;
    display: flex;
}

/* columns */
.sb-cols {
    flex: 1;
    min-height: 0;
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
}

/* 285px, down from 380px.
 *
 * An act column rarely gets the whole window. On the dashboard the sidebar
 * takes 165px, in the editor 145px plus whatever panel is open, and the
 * Unassigned rail takes another 232px on top of that -- so at 380px a 1366px
 * laptop fitted two columns and scrolled for the third, which is the one shape
 * a three-act board most needs to show.
 *
 *   1366px  ->  ~979px of act space  ->  2 columns at 380, 3 at 285
 *   1280px  ->  ~893px               ->  2 columns at 380, 3 at 285
 *   1024px  ->  ~637px               ->  1 column  at 380, 2 at 285
 *
 * flex-grow is still 1, so on a wide screen the columns expand to fill and
 * nothing looks cramped -- 285 is the floor, not the usual width. */
.sb-col {
    flex: 1 0 285px;
    min-width: 285px;
    max-width: 520px;
    display: flex;
    flex-direction: column;
    min-height: 0;
    border-right: 2px solid var(--sb-rule);
}

.sb-act {
    flex: none;
    background: var(--sb-ink);
    color: var(--sb-bg);
    padding: 12px 16px 14px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sb-act-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.sb-act-name {
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.sb-act-meta {
    margin-left: auto;
    font-size: 11px;
    letter-spacing: .1em;
    text-transform: uppercase;
    opacity: .65;
}

.sb-act-bar {
    height: 4px;
    background: rgba(243, 242, 242, .25);
}

.sb-act-bar span {
    display: block;
    height: 100%;
    background: var(--sb-accent);
}

.sb-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* beats */
.sb-beat {
    background: var(--sb-surface);
    border: 2px solid var(--sb-rule);
}

.sb-beat-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 10px 12px;
    border-bottom: 2px solid var(--sb-rule);
}

/* The name gives way first. Four things share this row at 11px -- chevron,
   name, meta, plus -- and the meta is the one that must not wrap, because a
   page total broken across two lines reads as two numbers. */
.sb-beat-name {
    min-width: 0;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--sb-accent-dark);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Scene count and page total, e.g. "4 scenes · 3 5/8 pgs." */
.sb-beat-meta {
    flex: none;
    margin-left: auto;
    font-size: 11px;
    font-weight: 500;
    color: var(--sb-muted);
    white-space: nowrap;
}

/* Fold a beat away. Sits left of the name so the chevrons point the way the
   beat moves, matching the holding rail's own collapse control. */
.sb-beat-collapse {
    flex: none;
    align-self: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sb-muted);
    font-size: 11px;
    line-height: 1;
    width: 12px;
    text-align: left;
    cursor: pointer;
    transition: color .12s ease;
}

.sb-beat-collapse:hover,
.sb-beat-collapse:focus-visible {
    color: var(--sb-accent-dark);
}

/* ---------------------------------------------------------------------------
 * Collapsed beat
 *
 * Same bargain as the collapsed holding rail: the cards go, the .sb-drop stays.
 * storyboard.js does not build the cards at all when a beat is shut -- which is
 * what actually shortens the column -- but it still emits the drop container
 * and its tail marker, so the beat remains a drop target. A folded beat you
 * cannot file a scene into would be useless precisely when it is most wanted,
 * which is when everything except the target has been folded away.
 *
 * The strip keeps enough height to be an easy target and still tints on hover.
 * ------------------------------------------------------------------------- */

.sb-beat.is-collapsed .sb-beat-head {
    border-bottom: 0;
}

.sb-beat.is-collapsed .sb-drop {
    min-height: 0;
    padding: 6px 10px 8px;
}

/* Tighter than the box in an expanded beat. At its usual 14px padding a folded
   beat costs ~50px, and thirteen of them would give back barely more height
   than they took -- which is the whole reason for folding. This lands near
   30px: unmistakably a target, still a fraction of the cards it replaced. */
.sb-beat.is-collapsed .sb-empty {
    padding: 7px 10px;
    font-size: 11px;
}

/* Add a scene straight into this group. Quiet until wanted -- it sits in every
   beat header, so at full colour it would out-shout the beat names it labels. */
.sb-add {
    flex: none;
    align-self: center;
    margin-left: 2px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sb-line);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    transition: color .12s ease, transform .12s ease;
}

.sb-add:hover,
.sb-add:focus-visible {
    color: var(--sb-accent-dark);
    transform: scale(1.15);
}

/* The whole beat brightens its plus on hover, so the affordance is findable
   without hunting for a 15px target. */
.sb-beat:hover .sb-add,
.sb-unassigned:hover .sb-add {
    color: var(--sb-muted);
}

.sb-beat:hover .sb-add:hover,
.sb-unassigned:hover .sb-add:hover {
    color: var(--sb-accent-dark);
}

.sb-drop {
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-height: 56px;
    transition: background .12s ease;
}

.sb-drop.is-over {
    background: var(--sb-accent-tint);
}

.sb-empty {
    border: 2px dashed var(--sb-line);
    padding: 14px 12px;
    font-size: 12px;
    color: var(--sb-muted);
}

/* cards */
.sb-slot {
    display: flex;
    flex-direction: column;
}

.sb-card {
    border: 2px solid var(--sb-line);
    border-radius: 5px;
    background: var(--sb-surface);
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 5px;
    user-select: none;
    transition: border-color .12s ease, box-shadow .12s ease;
}

/* Only cards you can actually move advertise themselves as draggable.
   touch-action:none is what lets Pointer Events see a drag instead of the
   browser turning it into a scroll. */
.sb-card.is-draggable {
    cursor: grab;
    touch-action: none;
}

.sb-card.is-draggable:hover {
    border-color: var(--sb-accent);
    box-shadow: 0 1px 0 rgba(32, 30, 29, .18);
}

.sb-card.is-dragging {
    opacity: .4;
    border-style: dashed;
    border-color: var(--sb-accent);
}

/* A just-saved card, flagged long enough to find after the board rebuilds. */
.sb-card.is-new {
    border-color: var(--sb-accent);
    animation: sb-new-card 1.8s ease-out;
}

@keyframes sb-new-card {

    0%,
    40% {
        background: var(--sb-accent-tint);
    }

    100% {
        background: var(--sb-surface);
    }
}

@media (prefers-reduced-motion: reduce) {
    .sb-card.is-new {
        animation: none;
        background: var(--sb-accent-tint);
    }
}

/* Selected cards, holding column only. A filled border rather than a tint, so a
   selection still reads at a glance against the .is-over drop highlight. */
.sb-card.is-selected {
    border-color: var(--sb-accent-dark);
    background: var(--sb-accent-tint);
    box-shadow: inset 3px 0 0 var(--sb-accent-dark);
}

/* Count on the drag ghost when a block is moving. */
.sb-ghost {
    position: fixed;
}

.sb-ghost-count {
    position: absolute;
    top: -9px;
    right: -9px;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border: 2px solid var(--sb-rule, #201e1d);
    background: #c9a84c;
    color: #201e1d;
    font-size: 11px;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
}

.sb-card-top {
    display: flex;
    align-items: baseline;
    gap: 8px;
}

/* Remove this card. Hidden until the card is hovered or focused, so a column of
   cards is not a column of trash cans -- but always present for keyboard and
   touch, where there is no hover to reveal it. */
.sb-del {
    flex: none;
    align-self: flex-start;
    margin-left: auto;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sb-line);
    font-size: 12px;
    line-height: 1.2;
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease, color .12s ease;
}

.sb-card:hover .sb-del,
.sb-card:focus-within .sb-del,
.sb-del:focus-visible {
    opacity: 1;
}

.sb-del:hover,
.sb-del:focus-visible {
    color: var(--sb-danger);
}

/* No hover on touch, so the icon has to stay put. */
@media (hover: none) {
    .sb-del {
        opacity: .55;
    }
}

/* Sized and weighted with the slugline it sits beside rather than shrunk away
   from it, and in the accent so it reads as the script's own numbering.

   No min-width: it was reserving 22px on every card to line the numbers up in a
   column, which only pays off when every card has one -- and most boards have
   none until the script is numbered. No letter-spacing either; that was
   compensating for 10px type. */
.sb-num {
    font-size: 14px;
    font-weight: 600;
    color: var(--sb-accent-dark);
}

/* No text-transform here on purpose. Headings are stored in caps by the save
   and import paths, so the card shows what is actually in the row -- a card
   reading in mixed case is a signal that something wrote around them. */
.sb-slug {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
}

/* No left indent. It used to line up under the slugline, past the scene number,
   which cost 30px on every card for an alignment nobody reads -- and hurt worst
   in the Unassigned rail, which is the narrowest column on the board. */
/* A synopsis is now a run of opening Action paragraphs rather than one, so the
   block is capped as well as spaced. Both matter: without the gap the
   paragraphs read as one undifferentiated wall, and without the cap a scene
   that opens on three long paragraphs makes a card taller than the beat holding
   it, which is the opposite of an index card.

   Six lines here, five in the narrower holding rail below. The full text is
   always a double-click away in the modal. */
.sb-synopsis {
    font-size: 13px;
    line-height: 1.2;
    color: #4a4644;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 6;
    line-clamp: 6;
    overflow: hidden;
}

/* renderSynopsis() emits one <p> per paragraph. Browser defaults would put a
   full blank line between them -- as much height as a line of text -- so the
   margin is reset and a deliberate small gap used instead: enough to separate
   two paragraphs, not enough to cost a line. */
.sb-synopsis p {
    margin: 0;
}

.sb-synopsis p+p {
    margin-top: 5px;
}

/* The holding column is narrower and holds the most cards, so its synopses are
   a size down and capped at five lines. Cards there are for scanning and
   placing; the full text is a double-click away in the modal. */
/* One step down from a board card in the narrow holding rail: 14/13 rather
   than 15/14. */
.sb-unassigned .sb-slug {
    font-size: 13px;
    line-height: 1.1;
}

/* One line tighter than a board card. The clamping machinery itself now lives
   on .sb-synopsis, so this only says what differs -- the rail is the narrowest
   column on the board and holds the most cards. */
.sb-unassigned .sb-synopsis {
    -webkit-line-clamp: 5;
    line-clamp: 5;
    font-size: 12px;
    line-height: 1.1;
}

/* Same step down as the synopsis above, for the same reason. */
.sb-unassigned .sb-chars {
    font-size: 11px;
    line-height: 1.25;
}

/* Who is in the scene. Three claims, three colours: the script says this person
   speaks, the script says they are present, the writer says they are present.
   Run together in one colour they would read as a single flat list and the
   distinction the scene report draws would be lost.

   Wrapping rather than clamped: a cast is a handful of short words and reads
   fine over two or three lines, unlike the synopsis it sits under. The comma
   separators are drawn in CSS so the names stay separate text nodes -- the card
   is built with textContent throughout and joining them into one string would
   put member-authored text back through innerHTML. */
.sb-chars {
    margin-top: 6px;
    font-size: 12px;
    line-height: 1.35;
    letter-spacing: .02em;
}

.sb-chars span+span::before {
    content: ", ";
    color: var(--sb-muted);
}

/* Speaking is the strongest claim, so it gets the ink. */
.sb-char-speak {
    color: var(--sb-ink);
    font-weight: 600;
}

/* Present but silent -- named in the action, no dialogue. Muted, as the scene
   report prints it. */
.sb-char-silent {
    color: var(--sb-muted);
}

/* The writer's own, which the script does not say. Given the board's accent so
   it reads as an annotation rather than as something extracted. */
.sb-char-added {
    color: var(--sb-accent-dark);
    font-style: italic;
}

.sb-board.is-compact .sb-chars {
    display: none;
}

/* ---- the cast section in the scene modal ------------------------------- */

/* Not inside .sb-board, so these cannot lean on the board's palette variables
   -- the modal is mounted at the document root. Same reason the list page has
   its own copy of them. */
/* One row for all three kinds -- speaking, silent, then the writer's own --
   wrapping as a single list. They were two containers, which put the added ones
   on a line of their own however few there were. */
/* No :empty rule to hide it when the script named nobody: the container is
   never truly empty -- PHP leaves whitespace in it -- and it does not need one,
   because whitespace generates no flex items and the row collapses to nothing
   on its own. */
.sb-chars-group .sb-cast-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* Not .sb-chip: that name is already taken further down by the structure tag on
   the board list, which is a 10px uppercase pill and nothing like this. */
.sb-cast-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    border-radius: 13px;
    font-size: 14px;
    line-height: 1.4;
    border: 1px solid transparent;
    background: #f3f2f2;
    color: #201e1d;
}

/* The three claims, told apart by fill rather than by ink. A row of chips is
   read as shapes before it is read as words, so the difference has to be in the
   thing the eye lands on first -- three shades of text on one background reads
   as one group with some emphasis in it, which is the opposite of the point. */

/* Speaking: solid, the strongest claim. */
.sb-cast-chip.is-speaking {
    background: #201e1d;
    border-color: #201e1d;
    color: #fff;
    font-weight: 600;
}

/* Present but silent: the same neutral family, filled in rather than solid. */
.sb-cast-chip.is-silent {
    background: #e2dedb;
    border-color: #cfc9c5;
    color: #4a4644;
}

/* The leading icon: a head speaking, or an eye for someone only seen. Quieter
   than the name it marks -- it is there to be scanned past, not read. */
.sb-cast-chip>i {
    font-size: 12px;
    opacity: .75;
}

/* The writer's own, in the board's accent, so it reads as an annotation rather
   than as something the script said. */
.sb-cast-chip.is-added {
    background: #f7f0dc;
    border-color: #c9a84c;
    color: #8a6f21;
}

/* Only the writer's own carry a remove control; the script's are read-only.

   One class is enough now. This used to be three deep to outrank a blanket
   `.modal360-content .dialogReset button` rule that padded every button in a
   dialog like a form field; that rule is gone, replaced by the opt-in
   `.dialog-field` in css/modal.css. */
.sb-cast-chip-x {
    border: 0;
    background: none;
    padding: 0;
    margin: 0 -3px 0 0;
    cursor: pointer;
    color: inherit;
    opacity: .6;
    font-size: 13px;
    line-height: 1;
}

.sb-cast-chip-x:hover {
    opacity: 1;
}

/* Sized to the names it holds rather than to the dialog. A cast list is a few
   short words, and a full-width select over one of them reads as a form field
   for something much bigger than it is. */
.sb-cast-pick {
    margin-top: 10px;
}

/* Three parts to the selector, not two. `.sb-cast-pick select` is (0,1,1), which
   only ties `.modal360-content select` in css/modal.css -- and modal.css is
   loaded after storyboard.css, so the tie went to it and neither the padding nor
   the size below applied. (0,2,1) settles it. */
.sb-chars-group .sb-cast-pick select {
    width: auto;
    min-width: 180px;
    max-width: 100%;
    /* .form-control sizes for a text field the width of the dialog; this holds
       one short name. */
    padding: .3rem;
    font-size: 15px;
}

/* Sits between the label and the chips, so it is read before the row it
   describes rather than after the control at the bottom. */
.sb-cast-note {
    margin: 2px 0 8px;
    font-size: 12px;
    line-height: 1.35;
    color: #6b6664;
}

/* Page and length, from the last sync. Quiet -- it is reference, not content. */
.sb-card-meta {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 11px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #8a8482;
}

/* margin-left rather than justify-content: space-between, so the length still
   sits hard right on a card that has a length but no page number. */
.sb-meta-len {
    margin-left: auto;
}

.sb-board.is-compact .sb-card-meta {
    display: none;
}

.sb-marker {
    height: 3px;
    margin-bottom: 2px;
    background: transparent;
}

.sb-marker.is-active {
    background: var(--sb-accent);
}

/* The card that follows the pointer. pointer-events:none matters -- without it
   the clone sits under the cursor and elementFromPoint only ever finds itself,
   so no drop target is ever resolved. */
.sb-ghost {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 4000;
    pointer-events: none;
    opacity: .95;
    border-color: #c9a84c;
    box-shadow: 0 6px 18px rgba(32, 30, 29, .28);
    background: #fff;
}

body.sb-dragging {
    cursor: grabbing;
}

body.sb-dragging .sb-card.is-draggable {
    cursor: grabbing;
}

/* ---------------------------------------------------------------------------
 * Unassigned -- the holding group
 *
 * Scenes that exist but have not been placed. Narrower than an act column,
 * because it is a staging area rather than part of the story's shape.
 * ------------------------------------------------------------------------- */

.sb-unassigned {
    flex: 0 0 230px;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    border-right: 2px solid var(--sb-rule);
    background: #ecebea;
}

.sb-unassigned-head {
    flex: none;
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 12px 12px 10px;
    border-bottom: 2px solid var(--sb-rule);
}

.sb-unassigned-name {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
}

.sb-unassigned-count {
    margin-left: auto;
    font-size: 11px;
    color: var(--sb-muted);
}

.sb-unassigned-drop {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    align-content: flex-start;
}

/* The collapse toggle. Louder than the plus beside it -- this one has to be
   findable when the rail is a 34px strip and nothing else is legible. */
.sb-collapse {
    flex: none;
    align-self: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sb-muted);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    transition: color .12s ease;
}

.sb-collapse:hover,
.sb-collapse:focus-visible {
    color: var(--sb-accent-dark);
}

/* ---------------------------------------------------------------------------
 * Collapsed holding column
 *
 * A narrow strip, not display:none. That is what keeps it a drop target: the
 * drag hit-tests with elementFromPoint and closest('.sb-drop'), so a rail that
 * is merely thin still accepts a card dragged out of the board. Hidden
 * outright, there would be nowhere to drop and the rail would have to expand on
 * hover mid-drag to compensate.
 *
 * The cards go; the .sb-drop stays, keeps its full height, and still tints on
 * hover and shows the insert marker.
 * ------------------------------------------------------------------------- */

.sb-unassigned.is-collapsed {
    flex: 0 0 34px;
    min-width: 34px;
}

.sb-unassigned.is-collapsed .sb-unassigned-head {
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 10px 2px;
    border-bottom: 0;
}

/* Turned on their side so the label and count read up the strip. */
.sb-unassigned.is-collapsed .sb-unassigned-name,
.sb-unassigned.is-collapsed .sb-unassigned-count {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    margin-left: 0;
    letter-spacing: .14em;
}

.sb-unassigned.is-collapsed .sb-unassigned-count {
    font-weight: 700;
    color: var(--sb-ink);
    letter-spacing: 0;
}

.sb-unassigned.is-collapsed .sb-unassigned-drop {
    padding: 4px 2px;
}

.sb-unassigned.is-collapsed .sb-slot,
.sb-unassigned.is-collapsed .sb-empty {
    display: none;
}

/* ---------------------------------------------------------------------------
 * Layouts (design handoff options 1a / 1b / 1d)
 *
 * All three render the same DOM -- acts > beats > scenes is invariant, which is
 * what the design deck meant by "four directions, same data model underneath".
 * So these are flex-direction changes, not repositioning: no absolute
 * positioning, no transforms, no coordinate maths.
 *
 * Drag is unaffected. targetAt() in storyboard.js hit-tests with
 * elementFromPoint + closest('.sb-drop'), so it reads whatever the browser has
 * laid out and never assumes an axis.
 *
 * 1a "columns" is the default above and needs no class.
 * ------------------------------------------------------------------------- */

/* 1b -- acts as swimlanes. Acts stack down the page, each a full-width lane
   with its title on a rail at the left and its beats running left to right.
   Reads like a timeline; suits teaser/act/tag television shapes. */
.sb-board.is-lanes .sb-cols {
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
}

.sb-board.is-lanes .sb-col {
    flex: none;
    flex-direction: row;
    min-width: 0;
    max-width: none;
    border-right: 0;
    border-bottom: 2px solid var(--sb-rule);
}

/* The act header becomes the rail. It is a flex column already, so it only
   needs a width and to stop stretching. */
.sb-board.is-lanes .sb-act {
    flex: 0 0 190px;
    justify-content: flex-start;
    padding: 14px 14px 16px;
}

.sb-board.is-lanes .sb-act-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
}

.sb-board.is-lanes .sb-act-meta {
    margin-left: 0;
}

.sb-board.is-lanes .sb-scroll {
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
    align-items: flex-start;
}

.sb-board.is-lanes .sb-beat {
    flex: 1 0 230px;
    min-width: 230px;
    align-self: stretch;
}

/* The beat header stacks in this layout, and only in this layout.
 *
 * Everywhere else a beat is as wide as its column -- 380px and up -- so the
 * chevron, name, scene count, page length and plus sit on one row with room to
 * spare. Here a beat is a 230px column, and that row needs about 260px: the
 * count-and-length alone runs to ~127px, which leaves the name a couple of
 * characters before it ellipses to nothing.
 *
 * So the meta drops to its own line. `order` rather than a source change,
 * because the plus belongs beside the name it adds to, not stranded under it:
 * line one is chevron, name, plus; line two is the count and length.
 */
.sb-board.is-lanes .sb-beat-head {
    flex-wrap: wrap;
    row-gap: 3px;
}

.sb-board.is-lanes .sb-beat-name {
    flex: 1 1 auto;
}

/* flex-basis:100% is what forces the wrap -- the item cannot share a line with
   anything, whatever width is going spare. */
.sb-board.is-lanes .sb-beat-meta {
    flex: 1 1 100%;
    order: 1;
    margin-left: 0;
}

/* No width override for a folded beat here, deliberately.
 *
 * It had one, and every candidate number was wrong. Narrow enough to be worth
 * having (170px) ellipsed the beat name to a few characters and pushed the plus
 * onto its own line; wide enough to stay readable (225px) differed from the
 * 230px above by five pixels, so folding twitched the column for no reason.
 *
 * A folded beat is therefore exactly as wide as an open one, and 230px is
 * stated once, in .sb-board.is-lanes .sb-beat above. What folding gives back in
 * this layout is height: a lane is as tall as its tallest beat, so folding one
 * changes little and folding all of them collapses the act to a band -- which
 * is the gesture that matters here anyway, putting an act you are not working
 * on out of the way. */

/* Unassigned needs no override in swimlanes.
 *
 * It was a capped grid band across the top, on the reasoning that a vertical
 * rail fights an axis already running downward. In practice the holding column
 * is the one thing that should not move between layouts: it is where you go to
 * find an unplaced scene, and hunting for it in a different place on each board
 * costs more than the tidier axis buys. .sb-body stays a row, so the rail sits
 * to the left of the stacked lanes exactly as it does in the other two.
 *
 * Nothing in the drag path cares. targetAt() hit-tests with elementFromPoint
 * and closest('.sb-drop'), so it reads whatever the browser laid out and never
 * assumes a direction.
 */

/* 1d -- continuous script order. One vertical document: acts are black bands,
   beats are ruled bands under them, and cards wrap left to right in reading
   order. Structure becomes annotation rather than architecture. */
.sb-board.is-flow .sb-cols {
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
}

.sb-board.is-flow .sb-col {
    flex: none;
    flex-direction: column;
    min-width: 0;
    max-width: none;
    border-right: 0;
}

.sb-board.is-flow .sb-scroll {
    overflow: visible;
    padding: 0;
    gap: 0;
}

/* Beats stop being boxes and become ruled bands. */
.sb-board.is-flow .sb-beat {
    border-width: 0 0 1px 0;
    border-bottom-style: dashed;
    border-bottom-color: var(--sb-line);
}

.sb-board.is-flow .sb-beat-head {
    border-bottom: 0;
    padding: 10px 14px 4px;
}

/* Scoped to .sb-cols throughout: the Unassigned rail sits beside this, stacks
   its cards vertically, and must keep the default treatment. */
.sb-board.is-flow .sb-cols .sb-drop {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
    padding: 8px 14px 14px;
}

/* Out-specifies the wrapping-row padding above, which would otherwise keep a
   folded band taller than it needs to be. The dashed box inside is already
   flex: 1 0 100% by the rule below, so it spans the band rather than sitting
   in it like a card. */
.sb-board.is-flow .sb-cols .sb-beat.is-collapsed .sb-drop {
    padding: 6px 14px 8px;
}

.sb-board.is-flow .sb-cols .sb-slot {
    flex: 0 0 220px;
    flex-direction: row;
    align-items: stretch;
}

.sb-board.is-flow .sb-cols .sb-slot .sb-card {
    flex: 1;
}

.sb-board.is-flow .sb-cols .sb-empty {
    flex: 1 0 100%;
}

/* The insert marker is a 3px horizontal rule in the stacked layouts. In a
   wrapping row it has to turn 90 degrees, or it points along the wrong axis and
   the drop position reads as ambiguous. */
.sb-board.is-flow .sb-cols .sb-marker {
    width: 3px;
    height: auto;
    align-self: stretch;
    margin-bottom: 0;
    margin-right: 2px;
}

/* compact density */
.sb-board.is-compact .sb-card {
    padding: 7px 10px;
    gap: 0;
}

/* 13px, not 12. Cabin Condensed runs small, and 12 was hard to read at a glance -- which
   defeats the point of compact, where you are scanning a lot of cards. */
.sb-board.is-compact .sb-slug {
    font-size: 13px;
}

.sb-board.is-compact .sb-synopsis {
    display: none;
}

.sb-board.is-compact .sb-drop {
    padding: 8px;
    gap: 5px;
}

/* footer */
.sb-foot {
    flex: none;
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 10px 24px;
    border-top: 2px solid var(--sb-rule);
    background: var(--sb-surface);
    font-size: 12px;
    color: var(--sb-muted);
}

.sb-foot .sb-status {
    margin-left: auto;
}

.sb-foot .sb-status.is-error {
    color: var(--sb-danger);
    font-weight: 600;
}

/* ---------------------------------------------------------------------------
 * Scene modal (popup/storyboardScene.php inside #modal360)
 *
 * The width is set here on the dialog rather than on .modal360-content for two
 * reasons: resizeModal() writes an inline `width: fit-content`, which would win
 * over any width in modal.css, and .modal360-content is shared by every modal
 * on the site, so widening it would widen Contact Us and the rest too. Sizing
 * the dialog instead means fit-content resolves to 800px, and the existing
 * `max-width: 90%` on .modal360-content (css/modal.css) still caps it on
 * narrow screens.
 * ------------------------------------------------------------------------- */

.sb-scene-dialog {
    width: 800px;
    max-width: 100%;
}

/* Narrower than the scene dialog: one text field, and 800px of it would leave
   the input looking like a mistake. Same reasoning about resizeModal()'s inline
   fit-content applies -- the width goes on the dialog, not on .modal360-content,
   which every modal on the site shares. */
.sb-board-dialog {
    width: 460px;
    max-width: 100%;
}

.sb-board-dialog .form-group {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    margin-bottom: 14px;
}

.sb-board-dialog .form-group label {
    display: block;
    width: 100%;
    margin-bottom: 4px;
}

.sb-board-dialog .form-group input {
    width: 100%;
}

.sb-scene-dialog .form-group {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    margin-bottom: 14px;
}

.sb-scene-dialog .form-group label {
    display: block;
    width: 100%;
    margin-bottom: 4px;
}

/* The heading's three parts on one line. The location takes what is left after
   the two qualifiers, which are sized to their content -- "INT./EXT." and
   "MOMENTS LATER" are the widest realistic values. */
.sb-scene-dialog .sb-slug-row {
    display: flex;
    align-items: stretch;
    gap: 8px;
    width: 100%;
}

.sb-scene-dialog .sb-slug-field {
    display: flex;
}

.sb-scene-dialog .sb-slug-field input {
    width: 100%;
}

.sb-scene-dialog .sb-slug-intext {
    flex: 0 0 110px;
}

.sb-scene-dialog .sb-slug-location {
    flex: 1 1 auto;
    min-width: 0;
}

.sb-scene-dialog .sb-slug-daynight {
    flex: 0 0 150px;
}

/* Typing shows caps immediately. The stored value is uppercased server-side in
   saveStoryboardScene.php, so this is feedback rather than the mechanism --
   what you see really is what is in the row. */
.sb-scene-dialog .sb-slug-row input {
    text-transform: uppercase;
    font-weight: 600;
    letter-spacing: .01em;
}

.sb-scene-dialog .sb-slug-row input::placeholder {
    text-transform: none;
    font-weight: 400;
    opacity: .55;
}

/* A heading the screenplay owns. Muted and not-allowed rather than hidden or
   removed -- the writer still needs to read it, and often to copy it, on the way
   to changing it in the script. */
.sb-scene-dialog .sb-slug-row.is-locked input {
    background: #f3f2f2;
    color: #6b6664;
    cursor: not-allowed;
}

.sb-scene-dialog .sb-locked-note {
    display: flex;
    align-items: baseline;
    gap: 7px;
    margin: 8px 0 0;
    font-size: 12px;
    line-height: 1.35;
    color: #6b6664;
}

.sb-scene-dialog .sb-locked-note i {
    flex: none;
    font-size: 11px;
    color: #ad8f39;
}

@media (max-width: 560px) {

    /* Three fields cannot share a line on a phone; the qualifiers pair up and
       the location takes its own row. */
    .sb-scene-dialog .sb-slug-row {
        flex-wrap: wrap;
    }

    .sb-scene-dialog .sb-slug-location {
        flex: 1 1 100%;
        order: -1;
    }
}

/* board picker */
.sb-list {
    display: flex;
    flex-direction: column;
    width: 100%;
    /* min-height:0 so the scrolling body below can actually shrink -- a flex
       item defaults to min-height:auto and refuses to go under its content,
       which would push the page instead of scrolling inside the list. */
    min-height: 0;
    background: var(--sb-bg);
    color: var(--sb-ink);
    font-family: 'Cabin Condensed', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* Scrolling lives here rather than on .sb-cards, so a section heading scrolls
   away with its own boards. With it on each grid, every section scrolled inside
   its own box and the headings stayed pinned to nothing. */
.sb-list-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 16px 24px 24px;
}

.sb-list-section + .sb-list-section {
    margin-top: 26px;
}

/* The dashboard's block-head carries its own bottom margin; this only adds what
   a count on the right of it needs. */
.sb-section-count {
    flex: none;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--sb-muted, #6b6664);
    white-space: nowrap;
}

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

.sb-list .sb-head {
    border-bottom: 2px solid #201e1d;
}

.sb-empty-state {
    padding: 40px 24px;
    font-size: 14px;
    color: #6b6664;
}

.sb-empty-state p {
    margin: 0 0 8px;
}

/* Padding and scrolling moved to .sb-list-body -- the grid is now one section's
   worth of cards, not the whole page. */
.sb-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 14px;
}

/* A div now, not an anchor: the card holds a link of its own (the script name),
   and nesting anchors is invalid. cursor:pointer is what it loses by not being
   one, since the whole card is still clickable. */
.sb-boardcard {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 14px 16px;
    border: 1px solid #201e1d;
    border-radius: 5px;
    background: #fff;
    text-decoration: none;
    color: inherit;
    cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease;
}

.sb-boardcard:hover {
    border-color: #c9a84c;
    box-shadow: 0 1px 0 rgba(32, 30, 29, .18);
}

/* The card's real link. Undecorated, because the whole card behaves as one
   target anyway -- this exists so the board is reachable by keyboard,
   middle-click and copy-link, not to look like a link inside a link. */
.sb-boardcard-title {
    font-size: 15px;
    font-weight: 700;
    color: inherit;
    text-decoration: none;
}

.sb-boardcard-meta {
    font-size: 12px;
    color: #6b6664;
}

/* This one does look like a link, because it goes somewhere else entirely --
   the script's own page, not the board. Without the distinction it would read
   as part of the card's subtitle and nobody would click it. */
.sb-boardcard-script {
    color: var(--sb-accent-dark, #ad8f39);
    font-weight: 600;
    text-decoration: none;
}

.sb-boardcard-script:hover,
.sb-boardcard-script:focus-visible {
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Whose script this is. Quieter than the meta line above it -- it is
   attribution, not a property of the board -- and it sits directly under the
   script name it refers to. */
.sb-boardcard-shared {
    display: flex;
    align-items: baseline;
    gap: 6px;
    font-size: 11px;
    color: var(--sb-muted);
}

.sb-boardcard-shared i {
    flex: none;
    font-size: 10px;
    color: var(--sb-line);
}

/* Structure and, when it applies, read-only. margin-top:auto pins them to the
   bottom so cards of differing title lengths still line their tags up. */
.sb-boardcard-tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: auto;
    padding-top: 2px;
}

/* Pushed to the far end of the tag row, and hidden until the card is
   approached -- a grid of boards should not read as a grid of trash cans. Kept
   present for keyboard and touch, where there is no hover to reveal it. Same
   bargain as .sb-del on a scene card. */
.sb-boardcard-del {
    flex: none;
    margin-left: auto;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sb-line);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease, color .12s ease;
}

.sb-boardcard:hover .sb-boardcard-del,
.sb-boardcard:focus-within .sb-boardcard-del,
.sb-boardcard-del:focus-visible {
    opacity: 1;
}

.sb-boardcard-del:hover,
.sb-boardcard-del:focus-visible {
    color: var(--sb-danger);
}

@media (hover: none) {
    .sb-boardcard-del {
        opacity: .55;
    }
}

/* The board's framework. Tinted rather than outlined -- it is a fact about the
   board, not a warning, and .sb-readonly beside it is the one that should read
   as a caveat. */
.sb-chip {
    flex: none;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--sb-accent-dark, #ad8f39);
    background: var(--sb-accent-tint, #f7f0dc);
    border-radius: 3px;
    padding: 2px 7px;
    line-height: 1.4;
    white-space: nowrap;
}

/* .sb-readonly is shared with the board header. It reads --sb-muted and
   --sb-line, which is why the palette above is declared on .sb-list as well as
   .sb-board. */

@media (max-width: 700px) {

    /* No height override here any more. It hardcoded 150px to compensate for
       the old measured constant; --header-height now governs at every width.
       No .sb-col override either: it narrowed the column from 380 to 300, and
       the base is 285 now -- so it had become a rule that made columns *wider*
       on the smallest screens. Nothing this narrow fits two columns beside the
       sidebar and the rail at any of these widths, so the width is the only
       thing it was deciding. */

    .sb-head {
        padding: 12px 14px;
    }

    .sb-foot {
        padding: 10px 14px;
    }
}