/* Workspace layout: header strip on top, then rail | chips | stage. Under 860px the rail becomes a bottom bar and the chips a horizontal strip, which is the same shape the phone app uses. */ .shell { display: flex; flex-direction: column; height: 100%; } .header { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--border-soft); background: var(--bg-elev); flex-wrap: wrap; } .brand { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; letter-spacing: 0.04em; color: var(--text); text-decoration: none; } .brand img { border-radius: 8px; } .brand em { font-style: normal; font-weight: 400; color: var(--text-dim); } /* The page name, top-left beside the mark — the same pair /admin and /profile carry, so every screen says where the visitor is. */ .page-name { font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); } .header .spacer { flex: 1 1 auto; } .recipe-name { font-family: var(--mono); font-size: 12px; color: var(--text-dim); border: 1px solid var(--border-soft); border-radius: 999px; padding: 3px 10px; max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .menu-wrap { position: relative; } .workspace { display: flex; flex: 1 1 auto; min-height: 0; } /* --- tool rail ---------------------------------------------------------- */ .rail { width: var(--rail-w); flex: 0 0 auto; border-right: 1px solid var(--border-soft); background: var(--bg-sunken); display: flex; flex-direction: column; padding: 6px; gap: 4px; overflow-y: auto; } .rail button { border: 1px solid transparent; background: transparent; border-radius: var(--radius-sm); padding: 9px 4px; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 10px; letter-spacing: 0.06em; color: var(--text-dim); } .rail button .glyph { font-size: 17px; line-height: 1; } .rail button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line); } .rail button:hover { color: var(--text); } /* --- panel columns ------------------------------------------------------- */ /* The panel is a cascade: the rail holds the tabs, then one column per open level — the tab's chips, the open chip's sub-chips, the open ruler. A child column never hides the column it came from, so the path reads left to right. */ .chips { flex: 0 0 auto; max-width: 62vw; display: flex; flex-direction: row; align-items: stretch; border-right: 1px solid var(--border-soft); background: var(--bg); overflow-x: auto; overflow-y: hidden; } .col { width: var(--col-w); flex: 0 0 auto; display: flex; flex-direction: column; gap: 8px; padding: 10px; overflow-y: auto; border-right: 1px solid var(--border-soft); } .col:last-child { border-right: none; } .col-slider { width: var(--slider-w); } /* CREATE RECIPES draws a two-up form, so its column is the wide one. */ .col-create { width: 300px; } .col-sub { background: var(--bg-elev); } .chip-row { display: flex; flex-wrap: wrap; gap: 6px; } /* In a column the chips stack: one chip per row, full width. */ .col .chip-row { flex-direction: column; align-items: stretch; } .col .chip { justify-content: space-between; text-align: left; } .wm-extra { border-right-color: var(--accent-line); } .chip { border: 1px solid var(--border); background: var(--bg-elev); border-radius: 999px; padding: 5px 11px; font-size: 12px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; } .col-sub .chip { background: var(--bg); } .chip[aria-pressed="true"], .chip.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); } /* Edited value, strip closed: the text glows amber, the border does not. */ .chip.amber, .chip.amber .val { color: var(--accent); } .chip .val { color: var(--text-dim); font-family: var(--mono); font-size: 11px; } .chip .pro { font-size: 9px; letter-spacing: 0.08em; color: var(--accent); } .chip:disabled { opacity: 0.4; cursor: not-allowed; } /* The font group as a dropdown: one row that shows the current pairing instead of three chips that all have to fit. */ .font-select { display: block; width: 100%; margin-top: 8px; padding-right: 26px; appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%239aa2ad' stroke-width='1.5'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; } .font-select option { background: var(--bg-elev); color: var(--text); } /* The ruler column: "<" back button, label + live value, slider, colour swatch for the knobs that drive a hue (WB's TEMPERATURE / TINT). */ .slider-row { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 10px; display: flex; align-items: center; gap: 10px; } .col .slider-row { flex-direction: column; align-items: stretch; gap: 6px; } .slider-row .ruler { flex: 1 1 auto; min-width: 0; } .slider-row .head { display: flex; justify-content: space-between; font-size: 11px; letter-spacing: 0.1em; color: var(--text-dim); } .slider-row .head b { color: var(--text); font-family: var(--mono); letter-spacing: 0; } .slider-row .head b.amber { color: var(--accent); } .slider-row .swatch { height: 26px; border: 1px solid var(--border); border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; } .slider-row .swatch span { font-family: var(--mono); font-size: 10px; letter-spacing: 0.06em; color: #fff; mix-blend-mode: difference; max-width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .slider-row .back { flex: 0 0 auto; align-self: flex-start; border: 1px solid var(--border); background: var(--bg-elev); color: var(--text-dim); border-radius: 999px; padding: 4px 10px; font-family: var(--mono); font-weight: 700; cursor: pointer; } input[type="range"] { width: 100%; accent-color: var(--accent); } .hint { color: var(--text-dim); font-size: 12px; line-height: 1.5; } /* --- CREATE RECIPES form (the phone's RecipeCreateModal, unrolled) ------- */ .create-form { border-top: 1px solid var(--border-soft); padding-top: 8px; display: flex; flex-direction: column; gap: 6px; } .create-label { font-size: 10px; letter-spacing: 0.12em; color: var(--text-dim); font-weight: 600; } .create-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; } .create-num { display: flex; flex-direction: column; gap: 3px; min-width: 0; } .create-num span { font-size: 10px; letter-spacing: 0.12em; color: var(--text-dim); font-weight: 600; } .create-num.wide { grid-column: 1 / -1; } .create-shift { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .create-shift .field { width: 64px; } .create-actions { display: flex; gap: 6px; margin-top: 4px; } .create-actions .btn { flex: 1 1 auto; } /* --- stage -------------------------------------------------------------- */ .stage { flex: 1 1 auto; min-width: 0; background: var(--bg-sunken); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 16px; position: relative; } .dropzone { border: 2px dashed var(--border); border-radius: var(--radius); padding: 40px 28px; text-align: center; color: var(--text-dim); cursor: pointer; background: var(--bg); max-width: 520px; } .dropzone.over { border-color: var(--accent); color: var(--text); background: var(--accent-soft); } .dropzone strong { display: block; color: var(--text); margin-bottom: 6px; font-size: 16px; } .canvas-wrap { position: relative; display: flex; align-items: center; justify-content: center; min-height: 0; flex: 1 1 auto; width: 100%; overflow: hidden; } .canvas-wrap img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: var(--radius-sm); box-shadow: var(--shadow); } /* Crop editor: the layer is the photo's own box, the rect is the framing and its shadow is what shades the part being cut away. */ /* Compare: the original left of the split, the grade right of it. The layer is the photo's own box and carries the stage's transform, so the bar stays put on the photo whatever the zoom or pan. */ .compare-layer { position: absolute; overflow: hidden; border-radius: var(--radius-sm); cursor: ew-resize; touch-action: none; } .compare-orig { position: absolute; inset: 0; width: 100%; height: 100%; box-shadow: none; } .compare-bar { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35); pointer-events: none; } .compare-bar::after { content: ''; position: absolute; left: 50%; top: 50%; width: 30px; height: 30px; transform: translate(-50%, -50%); border-radius: 999px; background: rgba(255, 255, 255, 0.94); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45); } .crop-layer { position: absolute; overflow: hidden; border-radius: var(--radius-sm); pointer-events: none; } .crop-rect { position: absolute; border: 1px solid var(--accent); box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5); cursor: move; pointer-events: auto; touch-action: none; } .crop-handle { position: absolute; width: 16px; height: 16px; background: var(--accent); border: 2px solid #fff; border-radius: 3px; touch-action: none; } .crop-handle.tl { left: 0; top: 0; transform: translate(-50%, -50%); cursor: nwse-resize; } .crop-handle.tr { right: 0; top: 0; transform: translate(50%, -50%); cursor: nesw-resize; } .crop-handle.bl { left: 0; bottom: 0; transform: translate(-50%, 50%); cursor: nesw-resize; } .crop-handle.br { right: 0; bottom: 0; transform: translate(50%, 50%); cursor: nwse-resize; } .busy { position: absolute; inset: auto 0 12px 0; margin: 0 auto; width: fit-content; background: var(--bg-elev); border: 1px solid var(--border); border-radius: 999px; padding: 5px 12px; font-size: 12px; color: var(--text-dim); box-shadow: var(--shadow); } .stage .toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: center; } .err { color: var(--danger); font-size: 12px; max-width: 520px; text-align: center; } /* --- auth dialog -------------------------------------------------------- */ .modal-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 60; padding: 16px; } .modal { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; width: 100%; max-width: 360px; box-shadow: var(--shadow); display: flex; flex-direction: column; gap: 10px; } .modal h2 { margin: 0; font-size: 17px; } /* --- strip moderation (/admin) ------------------------------------------ */ .adm { height: 100%; overflow-y: auto; padding: 0 22px 40px; } /* Sticky: the mark and the page name stay in the top-left corner while the photo grid scrolls under them, like the studio's header and the landing nav. */ .adm-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; position: sticky; top: 0; z-index: 5; margin: 0 -22px 16px; padding: 12px 22px; background: var(--bg); border-bottom: 1px solid var(--border-soft); } .adm-title { font-size: 15px; letter-spacing: 0.02em; } /* The mark and the page name, top-left, always a way back to the landing page — the same pair the studio's TopBar carries. */ .adm-brand { display: flex; align-items: center; gap: 6px; font-weight: 650; letter-spacing: -0.01em; color: var(--text); text-decoration: none; } .adm-brand em { font-style: normal; font-weight: 400; color: var(--text-dim); } .adm-brand img { border-radius: 8px; } .adm-slot { display: flex; flex-direction: column; gap: 3px; letter-spacing: 0.04em; } .adm-slot select { width: 100%; } .adm-spacer { flex: 1 1 auto; } .adm-note { margin: 12px 0; } .adm-sub { margin: 0 0 14px; } .adm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; } .adm-card { display: flex; flex-direction: column; gap: 8px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-elev); padding: 10px; } .adm-card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm); background: var(--bg-sunken); } .adm-meta { display: flex; flex-direction: column; gap: 2px; font-size: 11.5px; color: var(--text-dim); min-width: 0; } .adm-meta b { color: var(--text); font-family: var(--mono); } .adm-meta span { overflow-wrap: anywhere; } .adm-danger { color: var(--danger); border-color: var(--border); } .adm-danger:hover:not(:disabled) { border-color: var(--danger); } /* Settings drawer: the tabs that organise the page, plus the three panels. */ /* The management frame: a bordered box whose left column is the menu and whose right side is the pane for the chosen section. */ .adm-frame { display: flex; align-items: stretch; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-elev); overflow: hidden; } .adm-side { flex: 0 0 200px; display: flex; flex-direction: column; gap: 2px; padding: 12px 10px; border-right: 1px solid var(--border); background: var(--bg-sunken); } .adm-side-title { padding: 0 8px 8px; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); } .adm-side-item { display: block; padding: 8px 10px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text); font: inherit; text-align: left; text-decoration: none; cursor: pointer; } .adm-side-item:hover { background: var(--bg-elev); } .adm-side-item.on { background: var(--accent); color: var(--accent-fg); font-weight: 600; } .adm-pane { flex: 1 1 auto; min-width: 0; padding: 14px 16px; } .adm-table-wrap { overflow-x: auto; } /* Bulk selection: the three controls above the users table, and the checkbox column at its head (narrow so the picture column keeps the width). */ .adm-bulk { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; } .adm-bulk .btn { margin: 0; } .adm-check { width: 1%; white-space: nowrap; } .adm-check input { display: block; cursor: pointer; } .adm-face { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 999px; overflow: hidden; background: var(--bg-sunken); border: 1px solid var(--border); color: var(--text-dim); font-size: 11px; font-weight: 600; } .adm-face img { display: block; width: 100%; height: 100%; object-fit: cover; } .adm-filter { display: flex; align-items: center; gap: 8px; } /* Moderation state next to the account name, and the row's own actions: the three controls sit side by side and the pane scrolls when it runs out. */ .adm-badge-warn { margin-left: 6px; color: var(--danger); font-size: 11px; } .adm-table td:last-child { white-space: nowrap; } .adm-table td:last-child .btn { margin-right: 6px; } .adm-form { display: flex; flex-direction: column; gap: 8px; max-width: 360px; } .field-label { font-size: 11.5px; color: var(--text-dim); } /* --- profile picture ---------------------------------------------------- */ /* One face for every control that names the signed-in user: the workspace TopBar, the landing chip and the profile form. No picture = initial. */ .avatar { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 26px; height: 26px; border-radius: 999px; overflow: hidden; background: var(--bg-sunken); border: 1px solid var(--border); color: var(--text-dim); font-size: 11px; font-weight: 600; line-height: 1; } .avatar img { display: block; width: 100%; height: 100%; object-fit: cover; } .avatar-sm { width: 20px; height: 20px; font-size: 10px; } .avatar-row { display: flex; align-items: center; gap: 10px; } .avatar-row .avatar { width: 46px; height: 46px; font-size: 17px; } .account-btn { display: inline-flex; align-items: center; gap: 7px; } /* --- responsive --------------------------------------------------------- */ @media (max-width: 860px) { .workspace { flex-direction: column; } /* The frame's left column becomes a wrapping row above the pane. */ .adm-frame { flex-direction: column; } .adm-side { flex: 0 0 auto; flex-direction: row; flex-wrap: wrap; border-right: 0; border-bottom: 1px solid var(--border); } /* The header wraps on narrow screens, so a menu-wrap can land at the start of a row — the popover is right-anchored to its button and would hang off the left of the screen (measured: x=-280 of 390px). Anchor it to the full-width header instead. */ .header { position: relative; } .menu-wrap { position: static; } .popover { right: 8px; max-width: calc(100vw - 16px); } .rail { order: 2; width: auto; flex-direction: row; border-right: none; border-top: 1px solid var(--border-soft); overflow-x: auto; padding: 4px; } .rail button { flex: 1 0 auto; min-width: 62px; } /* The cascade still runs left to right; the columns themselves scroll rather than the page, so the stage keeps its width. */ .chips { order: 1; width: auto; max-width: none; max-height: 42vh; border-right: none; border-bottom: 1px solid var(--border-soft); } .col { width: 148px; padding: 8px; } .col-slider { width: 200px; } .col-create { width: 260px; } .stage { order: 0; flex: 1 1 auto; } }