/* Make the create-photo app break out of the DXPR content container */
.tl-order-app {
  width: 100vw;
  max-width: none;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  background: #ced1cd;
}

.tl-app-header {
  padding: 28px 40px;
}

.tl-app-body {
  display: grid;
  /* 390 / 450+ / 490. The first column took 60px from the third so its
     heading sits on one line; the page is exactly as wide as it was. */
  grid-template-columns: 390px minmax(450px, 1fr) 490px;
  gap: 24px;
  align-items: start;
  padding: 28px 40px;
  box-sizing: border-box;
}

/* The right-hand column is two panels, one under the other. It is the grid
   item; they are not — the same arrangement the middle column uses, and for
   the same reason: a second grid ROW would wait for the tallest column. */
.tl-right-column {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

.tl-upload-section,
.tl-pricing-section,
.tl-story-title-section,
.tl-summary-section {
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 10px;
  padding: 20px;
}

.tl-upload-placeholder {
  border: 2px dashed #ccc;
  min-height: 330px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 20px;
}

.tl-photo-actions {
  display: flex;
  justify-content: space-between;
  margin-top: 10px;
}

.tl-return-to-project-button {
  display: block;
  width: fit-content;
  margin: 12px auto 0;
  background: #2e8b57;
  color: #fff;
  text-decoration: none;
  padding: 10px 18px;
  border-radius: 6px;
  font-weight: 600;
  text-align: center;
}

.tl-return-to-project-button:hover {
  background: #25754a;
  color: #fff;
}

.tl-summary-section {
  position: sticky;
  top: 0;
  min-height: 51vh;

  display: flex;
  flex-direction: column;
}

/* ===== SUMMARY ACTIONS ===== */

/**
 * The summary's buttons are ONE stack and are sized as one: full width, evenly
 * spaced, in the order they are meant to be considered. Before this, each was
 * sized by its own rule and no two agreed — Proceed to Cart took its width from
 * its text, Save for Later was 100%, and Delete was a third size again.
 *
 * Matches .tl-keepsake-actions on the other builder.
 */
.tl-summary-actions {
  margin-top: auto;
  padding-top: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.tl-summary-actions .tl-checkout-button,
.tl-summary-actions .tl-save-for-later-button,
.tl-summary-actions .tl-photo-delete-button {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 20px;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.4;
  border-radius: 6px;
}

.tl-checkout-button {

  background: #d8d8d8;
  color: #888;

}

.tl-checkout-button:disabled,
.tl-checkout-button.tl-not-ready {
  background: #d8d8d8;
  color: #777;
}

.tl-checkout-button:disabled {
  cursor: not-allowed;
}

/* Not-ready still responds to a click (it explains what's missing), unlike
   the disabled state during an actual in-flight submission — so it gets
   the normal pointer cursor, not the blocked one. */
.tl-checkout-button.tl-not-ready:not(:disabled) {
  cursor: pointer;
}

.tl-checkout-button:not(:disabled):not(.tl-not-ready) {
  background: #2e8b57;
  color: #fff;
  cursor: pointer;
}

.tl-checkout-button:not(:disabled):not(.tl-not-ready):hover {

  background: #25754a;

}

/* Deliberately no separate color/border rules here — .tl-save-for-later-button
   also carries .tl-checkout-button, so it shares that button's grey
   (not-ready) and green (ready) states exactly, appearing and changing at
   the same time as Proceed to Cart. Only spacing is added on top. */
.tl-checkout-button,
/**
 * Delete, wearing the same clothes as the two buttons above it: filled grey
 * while there is nothing to delete, filled green once a photo is here.
 *
 * Grey means NOT READY, not disabled — it stays clickable and says what is
 * missing. Matches .tl-keepsake-delete-button on the other builder.
 */
.tl-photo-delete-button {
  padding: 10px 20px;
  border: 0;
  border-radius: 6px;
  background: #d8d8d8;
  color: #777;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}

.tl-photo-delete-button:not(.tl-not-ready) {
  background: #2e8b57;
  color: #fff;
}

.tl-photo-delete-button:not(.tl-not-ready):hover,
.tl-photo-delete-button:not(.tl-not-ready):focus {
  background: #25754a;
}

/* Width and spacing come from .tl-summary-actions above, with the other two. */

.tl-save-for-later-form {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tl-save-for-later-intro {
  font-size: 20px;
  color: #777;
  margin: 0 0 4px;
}

.tl-save-for-later-email,
.tl-save-for-later-password {
  width: 100%;
}

.tl-save-for-later-submit {
  width: 100%;
  background: #2e8b57;
  color: #fff;
  cursor: pointer;
}

.tl-save-for-later-submit:disabled {
  background: #d8d8d8;
  color: #777;
  cursor: not-allowed;
}

.tl-save-for-later-message {
  font-size: 14px;
  color: #555;
  margin: 4px 0 0;
}

.tl-pricing-table {
  width: 100%;
  border-collapse: collapse;
}

.tl-pricing-table th,
.tl-pricing-table td {
  padding: 12px 10px;
  border-bottom: 1px solid #ddd;
  vertical-align: middle;
}

.tl-pricing-table th {
  text-align: left;
}

/* Size and colour now come from the shared stepper (css/stepper.css) so this
   page's control and the cart's are the same object. Only the width of the
   number is set here: each quantity is an individual gift, so it has to stay
   legible up to 999 without shifting the buttons around as the number grows. */
.tl-qty {
  width: 3ch;
  min-width: 3ch;
}

.tl-variation-thumb {
  width: 60px;
  height: 60px;
  object-fit: cover;
  border: 1px solid #ddd;
  border-radius: 6px;
}

.tl-upload-preview {
  display: none;
  margin-top: 15px;
}

.tl-upload-preview img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.tl-upload-placeholder {
  width: 100%;
  height: 320px;
}

.uppy-Dashboard-inner {
  width: 100% !important;
  height: 320px !important;
}

.uppy-Dashboard-AddFiles {
  border: 2px dashed #d7d7d7;
}

/**
 * Reviewing the photo: its own panel, full width under the three columns.
 *
 * Full width because the cards stay 300px — the size they have always been —
 * and at least three should be visible at once, which no single column can
 * give them. They used to sit under the upload box in the 330px first column,
 * one above another, where the second one fell below the fold.
 */
/* The middle column is two panels, one under the other. It is the grid item;
   they are not, which is what keeps the previews from becoming a second row. */
.tl-middle-column {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

/* Under the print options, in the same column, but its own panel. */
.tl-preview-section {
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 10px;
  padding: 20px;
  box-sizing: border-box;
  min-width: 0;
}

.tl-preview-section[hidden] {
  display: none;
}

/* Three across, whatever the column is worth: the cards share it rather than
   holding a fixed width and wrapping. minmax(0, 1fr) rather than 1fr, or the
   image inside would refuse to shrink and push the third card out. */
.tl-gift-previews {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
  align-items: start;
}

/* The heading order.js writes in belongs to the whole row, not to one card. */
.tl-preview-header {
  grid-column: 1 / -1;
}

/*
 * Left, like every other numbered section heading on the page — the numbers
 * only read as a sequence if they start on the same line down the page.
 *
 * Still flex, which is what keeps the number and the words on one baseline;
 * only where the row starts has changed. The CARDS below stay centred: their
 * own headings and status buttons sit over their own pictures.
 */
.tl-preview-header h3 {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

/*
 * The numbered steps. Repeated here rather than shared with keepsake.css for
 * the same reason the rest of the shell is: the two stylesheets are never on
 * screen together. Keep the two in step by hand — they are meant to look
 * identical.
 */
.tl-step {
  display: inline-block;
  margin-right: 12px;
  width: 34px;
  height: 34px;
  line-height: 34px;
  text-align: center;
  border-radius: 50%;
  background: #2e8b57;
  color: #fff;
  font-size: 19px;
  font-weight: 700;
  vertical-align: -6px;
}

/*
 * The story title is a section heading, and now reads as one.
 *
 * The <label> inside it is what made it look different: a label is styled by
 * the theme in its own right - its own size, weight and family - and that
 * styling won simply by sitting closer to the text than the h3's did.
 * font: inherit hands it back, so the words take the heading's typography
 * exactly as the other numbered headings do, while remaining a label that
 * focuses the field when clicked.
 */
.tl-story-title-heading {
  display: flex;
  align-items: center;
  margin: 0 0 10px;
}

.tl-story-title-heading label {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  margin: 0;
  cursor: pointer;
}

/* The grey reassurance under the story title heading. Same look as
   .tl-keepsake-labels-note on the keepsake builder, which this page does not
   load, with the same room under it before the title box. */
.tl-photo-title-reassure {
  display: block;
  color: #555;
  font-size: 0.9em;
  margin-bottom: 10px;
}

.tl-gift-preview-image {
    display: block;
    width: 100%;
    max-width: 100%;
    margin: 8px auto 0;
    border-radius: 8px;
    cursor: pointer;
}

.tl-needs-review {
  background: #c62828;
  color: #fff;
}

.tl-review-completed {
  background: #2e7d32;
  color: #fff;
}

.tl-review-status-row {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/*
 * The status now sits between the title and the photograph, so it needs air
 * beneath it rather than above. Scoped to the preview card: the same status
 * row is used elsewhere and should not move.
 */
/*
 * A card's own heading, centred over its own picture.
 *
 * The card had no rule of its own at all, so the heading took the theme's
 * left alignment while the status button below it and the section heading
 * above it were both centred — the only thing on the card out of line.
 */
.tl-gift-preview-card h4 {
  margin: 0 0 8px;
  text-align: center;
}

.tl-gift-preview-card .tl-review-status-row {
  margin-bottom: 4px;
  display: flex;
  justify-content: center;
}

/* The counter under the story title: the only thing beneath the field, so it
   is sized to be read. Matches .tl-keepsake-counter on the other builder. */
.tl-photo-title-note {
  display: block;
  margin-top: 6px;
}

.tl-photo-counter {
  font-size: 16px;
  color: #888;
  font-variant-numeric: tabular-nums;
}

.tl-photo-counter-full {
  color: #a8541b;
  font-weight: 600;
}

/*
 * A low-resolution warning is the one thing on this card a customer must not
 * miss, so it stands as tall as the button beside it instead of reading as
 * punctuation after it. Scoped to the preview card — the cart and the order
 * tables use the same flag at their own size, and those were not asked about.
 */
.tl-gift-preview-card .tl-quality-flag {
  font-size: 26px;
  display: inline-flex;
  align-items: center;
}

.tl-quality-flag {
  display: inline-block;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  font-weight: 700;
  text-shadow: 0 0 1px currentColor;
}

.tl-quality-flag-fair {
  color: #FFC107;
}

.tl-quality-flag-poor {
  color: #F57C00;
}

.tl-quality-flag-bad {
  color: #E53935;
}

/* ---------- Review Dialog ---------- */

.tl-review-dialog {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 9999;
}


.tl-review-dialog-content {
  width: 92vw;
  max-width: 1120px;
  height: 90vh;
  max-height: 820px;
  margin: 5vh auto;
  background: #fff;
  border-radius: 10px;
  padding: 18px 20px;
  box-shadow: 0 10px 30px rgba(0,0,0,.35);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

.tl-review-dialog-title {
  margin-top: 0;
  margin-bottom: 8px;
  flex-shrink: 0;
}

.tl-review-dialog-subtitle {
  margin-top: -10px;
  margin-bottom: 12px;
  color: #555;
  flex-shrink: 0;
}

.tl-review-dialog-body {
  display: flex;
  align-items: stretch;
  gap: 30px;
  flex: 1 1 auto;
  min-height: 0;
}

.tl-review-dialog-image-wrap {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.tl-review-image-container {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tl-review-dialog-image {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 8px;
}

/* ===== CROP GRID (rule-of-thirds guide dots) ===== */
/* Cropper.js's own guide lines are a thin near-invisible dashed line
   (#eee at 50% opacity) that disappears against light photo backgrounds —
   restyled here as small alternating black/white dots so the grid stays
   visible regardless of what's under it. Only shown while the customer is
   actively adjusting the crop (dragging/resizing the crop box, or
   zooming) — see the cropstart/cropmove/zoom listeners in
   openReviewDialog() in order.js, which reveal it and re-hide it shortly
   after all of that stops. */

.tl-review-dialog .cropper-dashed {
  border: 0 !important;
  opacity: 1;
  transition: opacity 0.15s ease;
}

.tl-review-dialog .tl-grid-hidden .cropper-dashed {
  opacity: 0;
}

.tl-review-dialog .cropper-dashed.dashed-h::before,
.tl-review-dialog .cropper-dashed.dashed-h::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background-image:
    radial-gradient(circle, #000 42%, transparent 43%),
    radial-gradient(circle, #fff 42%, transparent 43%);
  background-size: 4px 2px, 4px 2px;
  background-position: 0 0, 2px 0;
  background-repeat: repeat-x;
}
.tl-review-dialog .cropper-dashed.dashed-h::before { top: -1px; }
.tl-review-dialog .cropper-dashed.dashed-h::after { bottom: -1px; }

.tl-review-dialog .cropper-dashed.dashed-v::before,
.tl-review-dialog .cropper-dashed.dashed-v::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background-image:
    radial-gradient(circle, #000 42%, transparent 43%),
    radial-gradient(circle, #fff 42%, transparent 43%);
  background-size: 2px 4px, 2px 4px;
  background-position: 0 0, 0 2px;
  background-repeat: repeat-y;
}
.tl-review-dialog .cropper-dashed.dashed-v::before { left: -1px; }
.tl-review-dialog .cropper-dashed.dashed-v::after { right: -1px; }

.tl-review-tools {
  width: 200px;
  flex-shrink: 0;
  background: #f7f7f7;
  border-radius: 10px;
  padding: 16px;

  display: flex;
  flex-direction: column;
}

.tl-review-tool-group {
  margin-bottom: 20px;
}

.tl-review-tool-group h4 {
  margin: 0 0 10px;
}

.tl-review-tool-group button {
  display: block;
  width: 100%;
  margin-bottom: 8px;
  padding: 9px 12px;
  cursor: pointer;
}

.tl-review-help {
  font-size: 14px;
  line-height: 1.5;
  color: #666;
}

.tl-review-dialog-actions {
  display: flex;
  justify-content: center;
  gap: 12px;

  margin-top: auto;
}

.tl-review-cancel,
.tl-review-complete {
  padding: 10px 18px;
  font-size: 15px;
  cursor: pointer;
}


/* ===== ZOOM CONTROLS ===== */

.tl-review-zoom {
  display: flex;
  flex-direction: column;
  width: 100%;
  margin-top: 12px;
  flex-shrink: 0;
}

.tl-zoom-label {
  display: block;
  width: 100%;
  text-align: center;
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 8px;
}

.tl-zoom-row {
  display: flex;
  align-items: center;
  width: 100%;
  gap: 12px;
}

.tl-zoom-row input[type="range"] {
  flex: 1;
  width: 100%;
}

/* Sits under the "Zoom" heading and above the slider it describes, so the
   instruction is read before the control rather than after it. */
.tl-zoom-help {
  display: block;
  width: 100%;
  text-align: center;
  margin-top: 0;
  margin-bottom: 8px;
  font-size: 20px;
  line-height: 1.35;
  color: #666;
}

.tl-zoom-minus,
.tl-zoom-plus {
  width: 28px;
  text-align: center;
  font-size: 20px;
  font-weight: 800;
  cursor: pointer;
  user-select: none;
}

/* ===== PRINT QUALITY WARNING ===== */

/*
 * Reserved space for the notice below the zoom slider.
 *
 * The photo above shares this column, so anything that changes height here
 * changes how much room the photo has, and Cropper re-fits the photo to the
 * room it is given. Keeping this slot the same height whether or not the
 * notice is showing is what keeps the photo still.
 *
 * A FIXED height, not a minimum, and the notice inside it is positioned
 * absolutely: a longer message on a narrow window then scrolls within the
 * slot instead of making the slot taller, which would bring the moving photo
 * straight back. 92px holds the tallest of the three messages with room to
 * spare at dialog width; the measured notice is 60px.
 */
.tl-print-warning-slot {
  position: relative;
  height: 92px;
  margin-top: 14px;
  flex-shrink: 0;
}

/* Only the notice inside the editor's reserved slot is pinned like this. The
   same block is used under the upload area, where it is an ordinary part of
   the page and must keep pushing what follows it. */
.tl-print-warning-slot .tl-print-warning {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  max-height: 100%;
  overflow: auto;
  margin-top: 0;
}

.tl-print-warning {
  display: none;
  align-items: flex-start;
  gap: 12px;

  margin-top: 14px;
  padding: 12px 14px;

  background: #1f1f1f;
  border: 2px solid #555;
  border-radius: 8px;
}

.tl-print-warning-text {
  color: #fff;
}

/* ===== REVIEW TOOL ICON BUTTONS ===== */

.tl-tool-icons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 12px;
}

.tl-tool-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  min-height: 76px;
  padding: 10px 6px;
  background: #fff;
  border: 1px solid #ccc;
  border-radius: 8px;
  cursor: pointer;
  text-align: center;
}

.tl-tool-icon {
  font-size: 24px;
  line-height: 1;
}

.tl-tool-text {
  font-size: 13px;
  line-height: 1.15;
  white-space: nowrap;
}

/* ===== ACTIVE TOOL BUTTON ===== */

.tl-tool-button.tl-active {
  background: #e8f2ff;
  border: 2px solid #2b6cb0;
  box-shadow: 0 0 0 1px rgba(43, 108, 176, 0.15);
}
/* ===== ORDER SUMMARY ===== */

.tl-summary-line {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 8px;
}

.tl-summary-line span {
  flex: 1;
}

.tl-summary-line strong {
  text-align: right;
  white-space: nowrap;
  min-width: 70px;
}

/* The gift itself, leading the summary above the prints it pays for. Set apart
   by a dotted rule rather than a heading, the same way the keepsake summary
   separates its story from its labels. */
.tl-summary-story {
  font-weight: 600;
  padding-bottom: 10px;
  margin-bottom: 10px;
  border-bottom: 1px dotted #cfcfcf;
}

/* What the charge is for, so the figure is never a number on its own. */
.tl-summary-story-note {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  font-weight: 400;
  color: #777;
}
.tl-summary-subtotal {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid #ddd;
  font-size: 18px;
  font-weight: 600;
}

/* ===== FAIR ===== */

.tl-print-warning-fair {
  border-color: #FFD54F;
}

.tl-print-warning-fair .tl-print-warning-icon,
.tl-print-warning-fair .tl-print-warning-text strong {
  color: #FFD54F;
}

/* ===== POOR ===== */

.tl-print-warning-poor {
  border-color: #FF9800;
}

.tl-print-warning-poor .tl-print-warning-icon,
.tl-print-warning-poor .tl-print-warning-text strong {
  color: #FF9800;
}

/* ===== VERY POOR ===== */

.tl-print-warning-bad {
  border-color: #FF5252;
}

.tl-print-warning-bad .tl-print-warning-icon,
.tl-print-warning-bad .tl-print-warning-text strong {
  color: #FF5252;
}

/*==== PHOTO PAGE NAME ====*/

.tl-photo-page-title {
  margin-top: 22px;
}

/* The field's own label only. The radio choices below are labels too, and this
   rule was making them bold blocks that read as headings rather than options. */
.tl-photo-page-title > label {
  display: block;
  font-weight: 600;
  margin-bottom: 8px;
}

.tl-photo-page-title input {
  width: 100%;
}

.tl-photo-page-help {
  margin-top: 6px;
  font-size: 20px;
  color: #777;
}
/* A story page that has been ordered keeps its title for good: labels and prints
   are already out there carrying a QR code that points at it, and renaming it here
   would rename the page they lead to. The box still shows the title, it just does
   not invite editing. */
.tl-title-locked {
  background: #f4f4f4;
  color: #555;
  cursor: not-allowed;
}

/* Reusing a purchased gift: the original story page, or a new one for someone
   else. Only ever shown for a project that has been bought. Indented, so the
   two choices read as answers to the heading above them rather than as two more
   things in the column. */
.tl-story-choice {
  margin: 4px 0 10px;
  padding-left: 18px;
}

.tl-story-option {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 5px 0;
  cursor: pointer;
}

/* The button comes first, whatever the theme's own form styling would rather
   do with it — order and width are both stated so it cannot be pushed after
   the words it belongs to or stretched across the row. */
.tl-story-option input[type="radio"] {
  order: -1;
  flex: none;
  width: auto;
  min-width: 0;
  margin: 3px 0 0;
  appearance: radio;
  -webkit-appearance: radio;
}

/* A choice, not a heading: stated so it cannot pick the field label's weight
   back up from anywhere. */
.tl-story-option-text {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 400;
  margin-bottom: 0;
}

/* The original page's own name, said plainly on the choice that keeps it. */
.tl-story-option-name {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: #333;
}

.tl-story-option-help {
  display: block;
  font-size: 13px;
  color: #666;
}
