:root {
    /* Functional Dark Theme (matches main Kittashi app) */
    --bg-base: #121212;
    --bg-panel: #222222;
    --bg-input: #1a1a1a;
    /* Sits between --bg-base and --bg-panel: used for Column 2's own
       container so it visually groups as a distinct drop surface,
       while the (lighter) file cards inside it still stand out. */
    --bg-elevated: #1c1c1c;
    --border-light: #3a3a3a;
    --border-hover: #555;

    --text-main: #eaeaea;
    --text-muted: #999;

    --c-purple: #8e44ad;
    --c-purple-hover: #9b59b6;
    --c-red: #c0392b;
    --c-red-hover: #e74c3c;
    --c-orange: #d35400;
    --c-orange-hover: #e67e22;
    --c-gray: #444;
    --c-gray-hover: #555;

    /* Module accent: Gang Sheet Builder deep blue (base #1C4B7E). Same
       three-tier hierarchy as DTF Prep's green, with each tier matched
       to its green counterpart's luminance so contrast against white
       text and the dark panels is unchanged. The --c-green/--c-blue
       names are kept from the shared palette so every existing rule
       re-themes without edits; --c-green is no longer green.
         --c-green*  bright CTA tier      (DTF #27ae60 / #2ecc71)
         --c-accent  standard tier        (DTF #147343) -- checkboxes
         --c-blue*   subdued base tier    (DTF #0F5934 / #188D53) */
    --c-green: #5B9CE4;
    --c-green-hover: #87B7EB;
    --c-accent: #2666AB;
    --c-blue: #1C4B7E;
    --c-blue-hover: #2F7CD1;
    /* Download/Export used a separate "genuinely blue" color while the
       accent was green; with a blue accent it shares the CTA tier. */
    --c-info: var(--c-green);
    --c-info-hover: var(--c-green-hover);
    /* Traffic-light status (good / warn / bad DPI) -- semantic, so it
       stays green regardless of the module accent. */
    --c-success: #2ecc71;

    --accent-link: #1C4B7E;
}

* {
    box-sizing: border-box;
}

/* Several components below set their own `display` (flex/grid)
   so they can lay out their children -- that unconditional
   `display` would otherwise beat the browser's default
   `[hidden] { display: none }` UA rule (same specificity, but
   author styles win ties over UA styles). Restated here with
   !important so toggling the `hidden` attribute from script.js
   always actually hides the element. */
[hidden] {
    display: none !important;
}

body {
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    margin: 0;
    background: var(--bg-base);
    color: var(--text-main);
    /* No height/overflow/flex constraints here anymore -- <body> is a
       normal, top-to-bottom-scrollable page now (needed so the FAQ/
       footer below .app-shell are actually reachable). Everything
       that used to rely on <body> being fixed to exactly one screen
       (the tool itself, with each of its own columns scrolling
       internally) now gets that from .app-shell below instead. */
}

/* The tool, exactly as it always behaved: fixed to one viewport-tall
   screen, its own internal panels scrolling independently, nothing
   about it visibly changes. Only the ELEMENT providing that behavior
   changed, from <body> to this wrapper -- see index.html -- so real
   page content (the FAQ/footer) can exist below it as an ordinary,
   scrollable continuation of the page instead of being trapped
   outside a hard `overflow: hidden` boundary. */
.app-shell {
    height: 100vh;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 20px;
    overflow: hidden;
}

/* --- FULL-BLEED HALFTONE HEADER --- */
.app-header {
    position: relative;
    flex-shrink: 0;
    height: 50px;
    margin: -20px -20px 0 -20px;
    overflow: hidden;
    display: flex;
    align-items: center;
    background-color: #121212;
    background-image: url('assets/halftone-strip.png');
    background-size: cover;
    background-position: right center;
    background-repeat: no-repeat;
    border-bottom: 1px solid var(--border-light);
}

.brand-block {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 12px;
    padding-left: 24px;
    height: 100%;
}

.brand-logo {
    display: block;
    height: 100%;
    max-height: 50px;
    width: auto;
    object-fit: contain;
    flex-shrink: 0;
    /* Returns to the dashboard -- see initDashboardLogoLink() in script.js. */
    cursor: pointer;
    filter: drop-shadow(0 0 4px rgba(28, 75, 126, 0.35));
}

/* App title beside the logo -- standardized across DTF Prep, Color
   Merge (.tagline there) and this app: 16.5px bold. */
.brand-title {
    font-size: 16.5px;
    font-weight: bold;
    color: var(--text-main);
    white-space: nowrap;
}

.brand-title span {
    color: var(--text-muted);
    font-weight: bold;
}

.brand-beta-badge {
    font-size: 12px;
    font-weight: normal;
    color: var(--text-muted);
    padding: 2px 8px;
    border: 1px solid #444;
    border-radius: 12px;
    margin-left: 10px;
}

/* Ported from the main Kittashi app's #btnFullscreenToggle -- solid
   pill (icon + label), an #id selector so nothing in this file's own
   cascade can quietly out-specificity it later on. Sits over the
   right side of .app-header's halftone strip; .app-header is only
   50px tall so this stays compact to avoid its own overflow:hidden
   clipping the button. */
#fullscreen-btn {
    position: absolute;
    top: 15px;
    right: 20px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    font-size: 12px;
    font-weight: 700;
    color: white;
    background: var(--c-blue);
    border: none;
    border-radius: 20px;
    cursor: pointer;
    transition: background 0.15s ease;
}

#fullscreen-btn:hover {
    background: var(--c-blue-hover);
}

#fullscreen-btn.is-fullscreen {
    background: var(--c-blue-hover);
}

/* --- SHARED FORM CONTROLS --- */
.panel-title {
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0;
    flex-shrink: 0;
}

.control-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.control-group label {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
}

select,
input[type="number"] {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border-light);
    background: var(--bg-input);
    color: var(--text-main);
    border-radius: 4px;
    font-family: inherit;
    font-size: 13px;
}

input[type="number"] {
    font-family: monospace;
}

select:focus,
input[type="number"]:focus {
    outline: none;
    border-color: var(--c-blue);
}

select {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M0 0L5 6L10 0Z' fill='%23999'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    padding-right: 28px;
    cursor: pointer;
}

/* Wraps a number input with a persistent unit label baked into the
   field itself, e.g. "150 [inches]" -- so a bare number typed into a
   Custom field can't be misread as some other unit (feet, in this
   app's case, since the tier options right above it show BOTH, like
   120" (10ft)). */
.input-with-suffix {
    position: relative;
    display: flex;
    align-items: center;
}

.input-with-suffix input[type="number"] {
    width: 100%;
    padding-right: 56px;
}

.input-suffix {
    position: absolute;
    right: 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    pointer-events: none;
}

/* --- TOGGLE SWITCH --- */
.toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.toggle-row .toggle-label {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.toggle-row .toggle-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
}

.toggle-row .toggle-sub {
    font-size: 11px;
    color: var(--text-muted);
}

.switch {
    position: relative;
    display: inline-block;
    width: 44px;
    height: 24px;
    flex-shrink: 0;
}

.switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.switch-track {
    position: absolute;
    inset: 0;
    background: var(--bg-input);
    border: 1px solid var(--border-light);
    border-radius: 999px;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.switch-track::before {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--text-muted);
    transition: transform 0.2s ease, background-color 0.2s ease;
}

.switch input:checked + .switch-track {
    background: rgba(91, 156, 228, 0.25);
    border-color: var(--c-green);
}

.switch input:checked + .switch-track::before {
    transform: translateX(20px);
    background: var(--c-green);
}

/* Smaller variant used inside a file card's override panel, where
   vertical space is tight. */
.switch-sm {
    width: 34px;
    height: 19px;
}

.switch-sm .switch-track::before {
    width: 13px;
    height: 13px;
}

.switch-sm input:checked + .switch-track::before {
    transform: translateX(15px);
}

/* --- ACTION BUTTONS ---
   One shared shape (size/padding/weight/transition); each variant
   below only overrides color, so the whole Column 1 action stack
   (Generate -> Download/Export -> Reset All) stays visually
   consistent while still color-coding what kind of action each one is:
   the module's bright CTA tier for "move forward" and "get your file"
   (both, as in DTF Prep), red for "this discards your work." */
.btn-primary,
.btn-info,
.btn-danger {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 13px 16px;
    border: none;
    border-radius: 4px;
    font-weight: 700;
    font-size: 14px;
    cursor: pointer;
    transition: background-color 0.15s ease, opacity 0.15s ease;
}

.btn-primary {
    background: var(--c-green);
    color: white;
}

.btn-primary:not(:disabled):hover {
    background: var(--c-green-hover);
}

/* generateBtn is now disabled while its running-kitten loading state
   is showing (see handleGenerateClick() in script.js) -- matches the
   existing .btn-info:disabled treatment below. :not(.is-loading) so
   THAT disabled state keeps its own full-strength look (see the
   kitten-loader block below) instead of getting dimmed by this one. */
.btn-primary:disabled:not(.is-loading) {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-info {
    background: var(--c-info);
    color: white;
}

.btn-info:not(:disabled):hover {
    background: var(--c-info-hover);
}

.btn-info:disabled:not(.is-loading) {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-danger {
    background: var(--c-red);
    color: white;
}

.btn-danger:hover {
    background: var(--c-red-hover);
}

/* --- SIGNATURE "RUNNING KITTEN" LOADER ---
   Class names/structure pulled verbatim from kittana-hd-upscaler's
   .model-upscale-btn loading state, generalized from that one
   hardcoded button class to .btn-primary/.btn-info so #generateBtn
   and #downloadBtn both get the exact same signature animation.
   Toggled by adding/removing .is-loading (see
   startButtonLoading()/stopButtonLoading() in script.js) -- both
   buttons already carry the required
   <span class="ai-btn-label">/<div class="ai-loading-scene"> markup
   (see index.html). The keyframes themselves were later rewritten to
   animate via `transform` instead of `left` for GPU compositing --
   see the comment above .ai-loading-scene. */
.btn-primary.is-loading,
.btn-info.is-loading {
    position: relative;
    overflow: hidden;
    cursor: wait;
    background-color: #7ec8e3;
}
.btn-primary.is-loading:hover,
.btn-info.is-loading:hover {
    background-color: #7ec8e3;
}

.btn-primary.is-loading .ai-btn-label,
.btn-info.is-loading .ai-btn-label {
    opacity: 0;
}

/* display:none both hides the scene when idle and halts/resets its
   own runKittenTranslate animation (and, since .kitten-gif is a CHILD
   of this element, its runKittenFlip animation too -- an ancestor's
   display:none pauses/resets descendant animations as well); re-adding
   .is-loading always restarts both from a clean state instead of
   resuming mid-cycle.
   The translate animation lives HERE, not on .kitten-gif itself, for a
   specifically correct reason: a percentage inside `transform:
   translate()` resolves against the ANIMATED ELEMENT'S OWN box, not
   its containing block -- unlike `left`/`right`, which resolve against
   the container. .kitten-gif is a small ~40px-wide image, so
   `calc(100% + 50px)` on IT would only ever travel ~90px total,
   nowhere near a real button's width. This element, though, has
   `inset: 0`, so its own box exactly equals the button's -- animating
   translate3d HERE makes `100%` correctly mean "the button's width."
   `will-change: transform` promotes it to its own compositor layer up
   front, so the browser hands the whole animation to the GPU instead
   of re-running layout/paint on the main thread every frame (the same
   thread the packing math runs on). */
.ai-loading-scene {
    display: none;
    position: absolute;
    inset: 0;
    pointer-events: none;
    will-change: transform;
    animation: runKittenTranslate 6s linear infinite;
}
.btn-primary.is-loading .ai-loading-scene,
.btn-info.is-loading .ai-loading-scene {
    display: block;
}

/* Runs left->right off-screen, pauses off-screen right for an
   imperceptible 0.1% (~6ms) beat while runKittenFlip (below) mirrors
   the actual image, then runs back right->left off-screen -- and gets
   a second flip for free on loop-restart, since 100%'s position
   matches 0%'s exactly. */
@keyframes runKittenTranslate {
    0% {
        transform: translate3d(-50px, 0, 0);
    }
    49.9% {
        transform: translate3d(calc(100% + 50px), 0, 0);
    }
    50% {
        transform: translate3d(calc(100% + 50px), 0, 0);
    }
    100% {
        transform: translate3d(-50px, 0, 0);
    }
}

/* The GIF's own internal leg-animation frames are fine (that's just
   the browser decoding the GIF, unrelated to CSS) -- this is only the
   left/right MIRROR flip, kept on the image itself (not the scene
   above) so it flips in place around its own center instead of
   dragging the image sideways by however wide the scene happens to
   be. Vertical centering can't ride along on `transform:
   translateY(-50%)` anymore -- this animation owns `transform`
   outright every frame -- so it's done with a static margin-top half
   the icon's own height instead. Timed identically to
   runKittenTranslate (same 6s, same 49.9%/50% snap point, same shared
   display:none reset from the ancestor above) so the flip always lines
   up with the moment the kitten reaches the right edge. */
.kitten-gif {
    position: absolute;
    top: 50%;
    left: 0;
    height: 38px;
    width: auto;
    margin-top: -19px;
    will-change: transform;
    animation: runKittenFlip 6s linear infinite;
}

@keyframes runKittenFlip {
    0% {
        transform: scaleX(1);
    }
    49.9% {
        transform: scaleX(1);
    }
    50% {
        transform: scaleX(-1);
    }
    100% {
        transform: scaleX(-1);
    }
}

/* =========================================================
   WORKSPACE -- 4-COLUMN GRID, FULL VIEWPORT HEIGHT
   ========================================================= */
.workspace {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 250px 350px minmax(320px, 1fr) 200px;
    gap: 16px;
}

/* ===================================================
   COLUMN 1 -- Upload, actions, global settings
   =================================================== */
.col-global {
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 14px;
    scrollbar-width: thin;
    scrollbar-color: var(--border-hover) var(--bg-base);
}

.action-row-vertical {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.settings-panel {
    flex-shrink: 0;
    background: var(--bg-panel);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    box-shadow: 0 4px 10px rgba(0,0,0,0.3);
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* ===================================================
   COLUMN 2 -- Uploaded file list (independent scroll)
   The whole column doubles as the drag-and-drop target
   (see script.js setupDropZone()), so it gets its own
   lighter panel treatment to read as a distinct drop
   surface rather than bare workspace background.
   =================================================== */
.col-files {
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    background: var(--bg-elevated);
    border: 2px dashed var(--border-light);
    border-radius: 8px;
    padding: 14px;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.col-files.drag-over {
    border-color: var(--c-green);
    background: rgba(91, 156, 228, 0.08);
}

/* Same cue when artwork is dragged over the preview (see
   wireDropTarget() in script.js), shown on the sheet area itself. */
.col-preview.drag-over .canvas-stage {
    border-color: var(--c-green);
    background-color: rgba(91, 156, 228, 0.08);
}

.upload-files-btn {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    padding: 8px 12px;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    background: var(--bg-input);
    color: var(--text-main);
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.upload-files-btn:hover {
    border-color: var(--c-green);
    background: rgba(91, 156, 228, 0.08);
}

.file-list-header {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-light);
}

.file-count {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
}

.file-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding-right: 6px;
    scrollbar-width: thin;
    scrollbar-color: var(--border-hover) var(--bg-base);
}

.file-list::-webkit-scrollbar { width: 8px; }
.file-list::-webkit-scrollbar-track { background: var(--bg-base); border-radius: 4px; }
.file-list::-webkit-scrollbar-thumb { background: var(--border-hover); border-radius: 4px; }
.file-list::-webkit-scrollbar-thumb:hover { background: #666; }

.file-list-empty {
    flex: 1;
    min-height: 160px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 30px 14px;
    text-align: center;
    color: var(--text-muted);
    cursor: pointer;
}

.file-list-empty svg {
    color: var(--c-green);
    margin-bottom: 4px;
}

.file-list-empty-title {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-main);
    line-height: 1.4;
}

.file-list-empty-hint {
    margin: 0;
    font-size: 11px;
    color: var(--text-muted);
    max-width: 240px;
}

/* Overrides .file-list-empty-hint's muted styling -- this specific
   hint (the 300 DPI default-sizing assumption) is easy to miss and
   easy to act on incorrectly, so it gets the warning palette instead
   of quiet grey to make sure it actually gets read. */
.dpi-alert-text {
    font-size: 12px;
    font-weight: 700;
    color: var(--c-orange-hover);
}

/* --- FILE CARD --- */
.file-card {
    flex-shrink: 0;
    background: var(--bg-panel);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    box-shadow: 0 4px 10px rgba(0,0,0,0.3);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Reserved for the soft-pixel (Warning B) check only -- low
   resolution is now a quiet inline DPI readout instead of a red
   card-wide alarm (that was too aggressive for a routine "you
   sized this a bit big" situation). Semi-transparent edge pixels
   are a real print defect, so they still get the loud treatment. */
.file-card.warning-state {
    border-color: var(--c-red);
    box-shadow: 0 0 0 1px var(--c-red) inset, 0 4px 10px rgba(0,0,0,0.3);
}

.file-card-row {
    display: flex;
    gap: 10px;
    align-items: flex-start;
}

.file-thumb {
    width: 52px;
    height: 52px;
    object-fit: contain;
    background: #fdfdfd;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    flex-shrink: 0;
}

.file-card-fields {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.file-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.file-inputs {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    flex-wrap: wrap;
}

.file-input-group {
    display: flex;
    flex-direction: column;
    gap: 3px;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
}

.file-input-group input {
    width: 62px;
    padding: 5px 6px;
    border: 1px solid var(--border-light);
    background: var(--bg-input);
    color: var(--text-main);
    border-radius: 4px;
    font-family: monospace;
    font-size: 12px;
    text-align: center;
}

.file-input-group input:focus {
    outline: none;
    border-color: var(--c-blue);
}

/* Live DPI readout + upsell link, sitting quietly to the right of
   the W/H inputs instead of the old red warning box -- this is meant
   to read as workstation telemetry (like a status LED), not an
   alarm. margin-left:auto pushes it flush right within .file-inputs,
   using the row's leftover horizontal space instead of stacking. */
.dpi-readout-group {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-left: auto;
    padding-bottom: 5px;
}

.dpi-readout {
    font-family: monospace;
    font-size: 12px;
    font-weight: 700;
    white-space: nowrap;
}

.dpi-readout.dpi-good {
    color: var(--c-success);
}

.dpi-readout.dpi-warn {
    color: #f0a868;
}

.dpi-readout.dpi-bad {
    color: var(--c-red-hover);
}

.subtle-link {
    font-size: 0.75rem;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.15s ease;
}

.subtle-link:hover {
    color: var(--c-green-hover);
    text-decoration: underline;
}

.file-remove-btn {
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: var(--bg-input);
    color: var(--text-muted);
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.file-remove-btn:hover {
    background: var(--c-red);
    color: white;
}

/* --- WARNING ROWS (shared look, per-file) --- */
.card-warning {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    background: rgba(211, 84, 0, 0.12);
    border: 1px solid var(--c-orange);
    border-radius: 6px;
    padding: 8px 10px;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.4;
    color: #f0a868;
}

.card-warning-text {
    flex: 1;
    min-width: 0;
}

.card-warning-link {
    display: inline-block;
    color: #A8C9EF;
    font-weight: 700;
    text-decoration: underline;
}

.card-warning-link:hover {
    color: var(--c-green-hover);
}

/* Warning B (soft pixels) needs a button + a follow-up link, unlike
   Warning A's single inline sentence -- forced onto its own full-width
   row (via flex-basis) below the warning text, rather than wrapping
   wherever there happens to be room in the row. */
.card-warning-actions {
    flex-basis: 100%;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}

.quick-fix-btn {
    flex-shrink: 0;
    width: auto;
    padding: 6px 12px;
    border: none;
    border-radius: 4px;
    background: var(--c-orange);
    color: white;
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.quick-fix-btn:hover {
    background: var(--c-orange-hover);
}

/* --- COPIES / GROUPING / OVERRIDE --- */
.card-group-section {
    border-top: 1px solid var(--border-light);
    padding-top: 10px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.card-meta-row {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 14px;
}

.group-toggle-inline {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    padding-bottom: 5px;
}

.group-toggle-inline input {
    accent-color: var(--c-accent);
    cursor: pointer;
}

/* Hides the *option* to override, not the override itself -- driven
   purely by "Group copies together" (see the fc-group-copies change
   handler in script.js), fully removed from layout like .override-body
   below. Whether an override is actually ACTIVE is a wholly separate
   concern the button inside here owns (see .override-body's own
   comment): checking the group box only reveals this wrapper, it never
   also turns the override on, and unchecking it forces the override
   back off before hiding this wrapper again. */
.override-wrapper {
    display: none;
}

.override-toggle-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    padding: 8px 12px;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    background: var(--bg-input);
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}

.override-toggle-btn:hover {
    border-color: var(--border-hover);
    color: var(--text-main);
}

.override-toggle-btn.expanded {
    border-color: var(--c-green);
    color: var(--text-main);
}

.override-chevron {
    display: inline-block;
    transition: transform 0.2s ease;
    font-size: 10px;
}

.override-toggle-btn.expanded .override-chevron {
    transform: rotate(180deg);
}

/* Fully removed from layout (display:none) until the user explicitly
   clicks "Override global settings" (see .override-toggle-btn's click
   handler / setOverrideExpanded() in script.js) -- NOT tied to "Group
   copies together" itself. That's the point: grouping only reveals the
   .override-wrapper above so the option exists, it never activates the
   override on its own, so a card can be grouped while still reading
   Global Settings live. */
.override-body {
    display: none;
    background: var(--bg-input);
    border-radius: 6px;
}

.override-body-inner {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 10px;
}

.toggle-row-compact {
    padding-top: 2px;
}

/* ===================================================
   COLUMN 3 -- Canvas preview
   =================================================== */
.col-preview {
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* Job-at-a-glance stats bar, RIP-software style -- see
   updatePreviewSummary() in script.js. flex-shrink: 0 keeps it a
   fixed short strip regardless of how tall .canvas-stage grows. */
.preview-summary {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    align-items: center;
    gap: 8px;
    background: rgba(91, 156, 228, 0.08);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    padding: 12px 16px;
}

.summary-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    text-align: center;
}

.summary-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
}

.summary-value {
    font-size: 16px;
    font-weight: 700;
    color: var(--text-main);
}

/* Two-column header above the preview canvas: title pinned left,
   background controls pinned right, on one row. flex-wrap lets the
   right-hand group drop to its own line on a narrow column instead of
   squeezing/overlapping the title. */
.preview-header-row {
    flex-shrink: 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}

/* View-only preference for how the preview canvas reads on screen --
   deliberately kept visually distinct from Global Settings (which all
   affect the actual print), and its own label says so explicitly. A
   tight inline group (not a full-width bar) since it now shares its
   row with the "Gang Sheet Preview" heading -- see .preview-header-row
   above. See applyPreviewBackground() in script.js. */
.preview-bg-toggle {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 10px;
}

.preview-bg-toggle-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
}

.preview-bg-toggle-sub {
    font-weight: 400;
    opacity: 0.75;
}

/* Segmented pill toggle: a row of radio inputs, visually hidden, each
   paired with a <span> that does all the actual rendering -- checked
   state is styled purely via the :checked + span sibling selector, no
   JS needed for the visual side of the toggle at all. */
.segmented-control {
    display: inline-flex;
    background: var(--bg-input);
    border: 1px solid var(--border-light);
    border-radius: 999px;
    padding: 2px;
    gap: 2px;
}

.segmented-option input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.segmented-option span {
    display: block;
    padding: 5px 14px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    border-radius: 999px;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.segmented-option:hover span {
    color: var(--text-main);
}

.segmented-option input:checked + span {
    background: var(--c-green);
    color: white;
}

.canvas-stage {
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    background: var(--bg-base);
    background-image:
        linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
    background-size: 24px 24px;
    border: 1px solid var(--border-light);
    border-radius: 8px;
    padding: 30px;
    overflow: auto;
}

/* Stacks one or more logical PAGES (each a .gang-sheet-page) top to
   bottom, with a real gap between them so a page break is obviously
   a page break, not just a coincidental seam. align-items: flex-start
   on .canvas-stage (not center) is deliberate: the whole stack can be
   many times taller than the viewport, and centering it would scroll
   the TOP out of view on load instead of starting the user there. */
.preview-container {
    display: flex;
    flex-direction: column;
    gap: 24px;
    max-width: 100%;
}

/* One physical sheet the customer would actually buy/print. The
   "paper" look (border/shadow) lives here rather than on
   .preview-container, since with pagination there are now multiple
   separate physical sheets, not one continuous roll -- the border is
   deliberately heavier/darker than ordinary panel borders so the
   break between pages reads as an obvious, intentional cut line.
   Background color is NOT set here -- it comes from whichever
   .preview-bg-* class applyPreviewBackground() has applied (see
   script.js), so DTF designs with white or black ink stay visible
   against a background the user actually chose, instead of always
   sitting on hardcoded white "paper." This is purely a CSS/display
   concern: the <canvas> pixel buffers underneath stay fully
   transparent, so PNG/PDF exports are never affected. */
.gang-sheet-page {
    display: flex;
    flex-direction: column;
    /* Fixed at 80% (not just capped by max-width) so the page's
       on-screen size no longer depends on whether its underlying
       canvas resolution (see PREVIEW_SCALE in script.js) happens to
       be wider or narrower than the column -- it's always 80%,
       centered. A page is normally much taller than it is wide, so
       trimming the DISPLAYED width also proportionally trims the
       displayed height, which is the whole win: more of a long gang
       sheet's length fits on screen before the user has to scroll. */
    width: 80%;
    max-width: 80%;
    margin: 0 auto;
    border: 3px solid var(--border-hover);
    border-radius: 2px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.5);
    overflow: hidden;
}

/* Medium/dark checkerboard (not the usual bright white/light-gray
   file-manager pattern) -- chosen specifically so BOTH pure white and
   pure black ink still read clearly against it, which a light
   checkerboard can't do for white ink. repeating-conic-gradient tiles
   4 alternating 90deg wedges per background-size box into a classic
   2x2 checker cell. */
.preview-bg-transparent {
    background-color: #666;
    background-image: repeating-conic-gradient(#777 0% 25%, #555 0% 50%);
    background-size: 24px 24px;
}

.preview-bg-black {
    background: #111;
}

.preview-bg-white {
    background: #fff;
}

/* Holds one or more stacked <canvas> chunks WITHIN a single page
   (script.js's "Seamless DOM Chunking") that together read as one
   continuous sheet -- a page only needs more than one of these if its
   own height exceeds CHUNK_HEIGHT_INCHES. display:block/no margin so
   they stack with zero gap between them (the seam has to be
   invisible), unlike the real gap between separate .gang-sheet-page
   elements above. */
.gang-sheet-chunk {
    display: block;
    margin: 0;
    padding: 0;
    max-width: 100%;
}

/* ===================================================
   COLUMN 4 -- Kittashi Suite funnel
   =================================================== */
.col-funnel {
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
    scrollbar-width: thin;
    scrollbar-color: var(--border-hover) var(--bg-base);
}

.funnel-card {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: var(--bg-panel);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    box-shadow: 0 4px 10px rgba(0,0,0,0.3);
    padding: 14px;
    text-decoration: none;
    color: var(--text-main);
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.funnel-card:hover {
    border-color: var(--c-green);
    box-shadow: 0 0 12px rgba(91, 156, 228, 0.35), 0 4px 10px rgba(0,0,0,0.3);
    transform: translateY(-1px);
}

.funnel-icon {
    font-size: 20px;
    line-height: 1;
}

.funnel-title {
    font-size: 12.5px;
    font-weight: 700;
    color: var(--text-main);
}

.funnel-sub {
    font-size: 11px;
    color: var(--text-muted);
    line-height: 1.4;
}

/* --- RESPONSIVE --- */
@media (max-width: 1300px) {
    .app-shell {
        overflow: auto;
    }

    .workspace {
        display: flex;
        flex-direction: column;
    }

    .col-global,
    .col-files,
    .col-preview,
    .col-funnel {
        overflow: visible;
    }

    .file-list {
        max-height: 420px;
    }

    .canvas-stage {
        min-height: 420px;
    }

    .col-funnel {
        flex-direction: row;
        flex-wrap: wrap;
    }

    .funnel-card {
        flex: 1 1 200px;
    }
}

/* ===================================================
   DEVICE LOCKS -- ported from the main Kittashi app's
   #mobile-device-gate / #tabletPortraitOverlay pattern.
   Hidden by default; opaque covers over the tool rather
   than display:none on .app-shell's contents, so nothing
   here disturbs canvas/viewport sizing once a device
   rotates back to landscape and these hide again.
   =================================================== */
#mobile-lock,
#portrait-lock {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    background: var(--bg-base);
    color: var(--text-main);
    padding: 20px;
    box-sizing: border-box;
}

/* Phone gate wins over the tablet-portrait gate on a phone held in
   portrait (where both could otherwise match at once) -- there's no
   point telling someone to rotate a screen that's too small either
   way. */
#mobile-lock {
    z-index: 9999;
}

#portrait-lock {
    z-index: 2000;
}

.mobile-gate-logo {
    max-width: 220px;
    width: 100%;
    height: auto;
    margin-bottom: 20px;
}

#mobile-lock h2 {
    margin: 0;
    max-width: 420px;
    color: var(--text-main);
    font-size: 22px;
    font-weight: 700;
}

#portrait-lock p {
    margin: 0;
    max-width: 420px;
    color: var(--text-muted);
    font-size: 16px;
    line-height: 1.6;
}

@media (max-width: 767px) {
    .app-shell {
        display: none;
    }

    #mobile-lock {
        display: flex;
    }
}

@media (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
    .app-shell {
        display: none;
    }

    #portrait-lock {
        display: flex;
    }
}

/* ===================================================
   SEO / LAUNCH CONTENT -- lives BELOW .app-shell, as
   ordinary scrollable page content (see body/.app-shell
   above), not inside the tool's own fixed-height layout.
   =================================================== */
.faq-section {
    max-width: 820px;
    margin: 48px auto;
    padding: 0 24px;
}

.faq-heading {
    font-size: 20px;
    font-weight: 700;
    color: var(--text-main);
    text-align: center;
    margin: 0 0 20px;
}

.faq-item {
    background: var(--bg-panel);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    padding: 14px 18px;
    margin-bottom: 10px;
}

.faq-item summary {
    cursor: pointer;
    font-weight: 700;
    font-size: 14px;
    color: var(--text-main);
    list-style: none;
    position: relative;
    padding-right: 28px;
}

/* Chrome/Safari's own disclosure triangle, replaced with the
   +/- below so every browser gets the same look. */
.faq-item summary::-webkit-details-marker {
    display: none;
}

.faq-item summary::after {
    content: '+';
    position: absolute;
    right: 2px;
    top: 0;
    color: var(--c-green);
    font-size: 18px;
    font-weight: 700;
    line-height: 1;
}

.faq-item[open] summary::after {
    content: '\2212';
}

.faq-item p {
    margin: 12px 0 0;
    color: var(--text-muted);
    font-size: 14px;
    line-height: 1.6;
}

#site-footer {
    text-align: center;
    padding: 24px 16px 32px;
    color: var(--text-muted);
    font-size: 12px;
    border-top: 1px solid var(--border-light);
}

#site-footer a {
    color: var(--c-green);
    text-decoration: none;
}

#site-footer a:hover {
    color: var(--c-green-hover);
    text-decoration: underline;
}
